Merge changes I79864ffd,I86422b7b,I7fd61193 into androidx-main
* changes:
Add XMethodElement#name to reflect source name
Add name property to XMethodElement
Rename XMethodElement.name to jvmName
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0e45536..cfc87ae 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -35,12 +35,19 @@
- Download and install JDK 11, if you don’t have it already.
+- Download and install the NDK
+
+ ```bash
+ sdkmanager --install "ndk;23.1.7779620"
+ ```
+
Next, you need to set up the following environment variables:
```bash
# You could also add this to your .{bash|zsh}rc file.
export JAVA_HOME="location of JDK 11 folder"
export ANDROID_SDK_ROOT="location of the Android SDK folder"
+ export ANDROID_NDK_ROOT="location of the Android NDK folder containing version 23.1.7779620"
```
### Checkout & Importing a Project
diff --git a/OWNERS b/OWNERS
index 8089367..5d06014 100644
--- a/OWNERS
+++ b/OWNERS
@@ -13,7 +13,6 @@
mount@google.com
nickanthony@google.com
owengray@google.com
-pavlis@google.com
romainguy@android.com
saff@google.com
sergeyv@google.com
diff --git a/activity/activity-compose/samples/lint-baseline.xml b/activity/activity-compose/samples/lint-baseline.xml
deleted file mode 100644
index 16095fa..0000000
--- a/activity/activity-compose/samples/lint-baseline.xml
+++ /dev/null
@@ -1,9779 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-beta02" type="baseline" client="gradle" dependencies="true" name="AGP (7.1.0-beta02)" variant="all" version="7.1.0-beta02">
-
- <issue
- id="WrongConstant"
- message="Must be one or more of: AccessibilityEventCompat.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_SUBTREE, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_TEXT, AccessibilityEventCompat.CONTENT_CHANGE_TYPE_UNDEFINED, but could be AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE, AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED, AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED"
- errorLine1=" return event.getContentChangeTypes();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="344"
- column="20"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: Action.SEMANTIC_ACTION_NONE, Action.SEMANTIC_ACTION_REPLY, Action.SEMANTIC_ACTION_MARK_AS_READ, Action.SEMANTIC_ACTION_MARK_AS_UNREAD, Action.SEMANTIC_ACTION_DELETE, Action.SEMANTIC_ACTION_ARCHIVE, Action.SEMANTIC_ACTION_MUTE, Action.SEMANTIC_ACTION_UNMUTE, Action.SEMANTIC_ACTION_THUMBS_UP, Action.SEMANTIC_ACTION_THUMBS_DOWN, Action.SEMANTIC_ACTION_CALL"
- errorLine1=" builder.setSemanticAction(action.getSemanticAction());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4801"
- column="47"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO, RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED, RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED"
- errorLine1=" ? remoteInput.getEditChoicesBeforeSending()"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="6443"
- column="31"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO, RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED, RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED"
- errorLine1=" ? src.getEditChoicesBeforeSending()"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="7476"
- column="35"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: Action.SEMANTIC_ACTION_NONE, Action.SEMANTIC_ACTION_REPLY, Action.SEMANTIC_ACTION_MARK_AS_READ, Action.SEMANTIC_ACTION_MARK_AS_UNREAD, Action.SEMANTIC_ACTION_DELETE, Action.SEMANTIC_ACTION_ARCHIVE, Action.SEMANTIC_ACTION_MUTE, Action.SEMANTIC_ACTION_UNMUTE, Action.SEMANTIC_ACTION_THUMBS_UP, Action.SEMANTIC_ACTION_THUMBS_DOWN, Action.SEMANTIC_ACTION_CALL"
- errorLine1=" actionBuilder.setSemanticAction(action.getSemanticAction());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompatBuilder.java"
- line="390"
- column="49"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: LineBreaker.BREAK_STRATEGY_SIMPLE, LineBreaker.BREAK_STRATEGY_HIGH_QUALITY, LineBreaker.BREAK_STRATEGY_BALANCED"
- errorLine1=" .setBreakStrategy(params.getBreakStrategy())"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="467"
- column="43"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: Layout.HYPHENATION_FREQUENCY_NORMAL, Layout.HYPHENATION_FREQUENCY_FULL, Layout.HYPHENATION_FREQUENCY_NONE"
- errorLine1=" .setHyphenationFrequency(params.getHyphenationFrequency())"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="468"
- column="50"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: RemoteInput.SOURCE_FREE_FORM_INPUT, RemoteInput.SOURCE_CHOICE"
- errorLine1=" return android.app.RemoteInput.getResultsSource(intent);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="531"
- column="20"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO, RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED, RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED"
- errorLine1=" builder.setEditChoicesBeforeSending(src.getEditChoicesBeforeSending());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="576"
- column="49"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO, RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED, RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED"
- errorLine1=" builder.setEditChoicesBeforeSending(src.getEditChoicesBeforeSending());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="598"
- column="49"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: LineBreaker.BREAK_STRATEGY_SIMPLE, LineBreaker.BREAK_STRATEGY_HIGH_QUALITY, LineBreaker.BREAK_STRATEGY_BALANCED"
- errorLine1=" textView.setBreakStrategy(params.getBreakStrategy());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/TextViewCompat.java"
- line="886"
- column="39"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: Layout.HYPHENATION_FREQUENCY_NORMAL, Layout.HYPHENATION_FREQUENCY_FULL, Layout.HYPHENATION_FREQUENCY_NONE"
- errorLine1=" textView.setHyphenationFrequency(params.getHyphenationFrequency());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/TextViewCompat.java"
- line="887"
- column="46"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: Callback.DISPATCH_MODE_STOP, Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE"
- errorLine1=" super(compat.getDispatchMode());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/WindowInsetsAnimationCompat.java"
- line="968"
- column="23"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one or more of: Type.STATUS_BARS, Type.NAVIGATION_BARS, Type.CAPTION_BAR, Type.IME, Type.WINDOW_DECOR, Type.SYSTEM_GESTURES, Type.MANDATORY_SYSTEM_GESTURES, Type.TAPPABLE_ELEMENT, Type.DISPLAY_CUTOUT"
- errorLine1=" return mController.getTypes();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/WindowInsetsAnimationControllerCompat.java"
- line="332"
- column="20"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_TOUCH, WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE, WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE"
- errorLine1=" return mImpl.getSystemBarsBehavior();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/WindowInsetsControllerCompat.java"
- line="275"
- column="16"/>
- </issue>
-
- <issue
- id="WrongConstant"
- message="Must be one of: WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_TOUCH, WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE, WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE, but could be WindowInsetsController.BEHAVIOR_DEFAULT"
- errorLine1=" return mInsetsController.getSystemBarsBehavior();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/WindowInsetsControllerCompat.java"
- line="730"
- column="20"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" requestRelaunchActivityMethod.invoke(activityThread,"
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/app/ActivityRecreator.java"
- line="152"
- column="21"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" performStopActivity3ParamsMethod.invoke(activityThread,"
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/app/ActivityRecreator.java"
- line="274"
- column="29"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" performStopActivity2ParamsMethod.invoke(activityThread,"
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/app/ActivityRecreator.java"
- line="277"
- column="29"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (IBinder) sGetIBinderMethod.invoke(bundle, key);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/BundleCompat.java"
- line="60"
- column="38"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" sPutIBinderMethod.invoke(bundle, key, binder);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/BundleCompat.java"
- line="84"
- column="21"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (String) getMethod.invoke(systemProperties, name);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/DisplayCompat.java"
- line="135"
- column="29"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (String) sGetScriptMethod.invoke(null, args);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/ICUCompat.java"
- line="116"
- column="33"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (String) sAddLikelySubtagsMethod.invoke(null, args);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/ICUCompat.java"
- line="133"
- column="33"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (int) icon.getClass().getMethod("getType").invoke(icon);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="1104"
- column="26"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (String) icon.getClass().getMethod("getResPackage").invoke(icon);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="1132"
- column="29"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (int) icon.getClass().getMethod("getResId").invoke(icon);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="1161"
- column="26"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (Uri) icon.getClass().getMethod("getUri").invoke(icon);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="1189"
- column="26"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (Boolean) sActionBarOnMenuKeyMethod.invoke(actionBar, event);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/KeyEventDispatcher.java"
- line="106"
- column="34"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" Boolean result = (Boolean) sMethodUserHandleIsAppMethod.invoke(null, uid);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/os/ProcessCompat.java"
- line="104"
- column="48"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" Boolean result = (Boolean) sMethodUserIdIsAppMethod.invoke(null, uid);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/os/ProcessCompat.java"
- line="142"
- column="48"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" sRebaseMethod.invoke(theme);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
- line="704"
- column="29"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" removeItemAtMethod.invoke(menu, i);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/TextViewCompat.java"
- line="625"
- column="25"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (Typeface) sCreateFromFamiliesWithDefault.invoke("
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi21Impl.java"
- line="127"
- column="31"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" final Boolean result = (Boolean) sAddFontWeightStyle.invoke("
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi21Impl.java"
- line="138"
- column="46"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" final Boolean result = (Boolean) sAddFontWeightStyle.invoke("
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi24Impl.java"
- line="112"
- column="46"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (Typeface) sCreateFromFamiliesWithDefault.invoke("
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi24Impl.java"
- line="124"
- column="31"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (Boolean) mAddFontFromAssetManager.invoke(family,"
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="140"
- column="30"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (Boolean) mAddFontFromBuffer.invoke(family,"
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="155"
- column="30"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (Typeface) mCreateFromFamiliesWithDefault.invoke(null /* static method */,"
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="171"
- column="31"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (Boolean) mFreeze.invoke(family);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="183"
- column="30"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" mAbortCreation.invoke(family);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="194"
- column="13"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (Typeface) mCreateFromFamiliesWithDefault.invoke(null /* static method */,"
- errorLine2=" ^">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi28Impl.java"
- line="51"
- column="31"/>
- </issue>
-
- <issue
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check"
- errorLine1=" return (Boolean) sIsProjectedDrawableMethod.invoke(mDrawable);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/WrappedDrawableApi21.java"
- line="131"
- column="34"/>
- </issue>
-
- <issue
- id="PrivateConstructorForUtilityClass"
- message="Utility class is missing private constructor"
- errorLine1="public class NotificationCompat {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="82"
- column="14"/>
- </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() {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2164"
- column="40"/>
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2179"
- column="17"
- message="Setter here"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="The getter return type (`CollectionInfoCompat`) and setter parameter type (`Object`) getter and setter methods for property `collectionInfo` 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 CollectionInfoCompat getCollectionInfo() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2988"
- column="33"/>
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2998"
- column="17"
- message="Setter here"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="The getter return type (`CollectionItemInfoCompat`) and setter parameter type (`Object`) getter and setter methods for property `collectionItemInfo` 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 CollectionItemInfoCompat getCollectionItemInfo() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3021"
- column="37"/>
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3007"
- column="17"
- message="Setter here"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="The getter return type (`AccessibilityNodeInfoCompat`) and setter parameter type (`View`) getter and setter methods for property `labelFor` 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 getLabelFor() {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3262"
- column="40"/>
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3225"
- column="17"
- message="Setter here"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="The getter return type (`AccessibilityNodeInfoCompat`) and setter parameter type (`View`) getter and setter methods for property `labeledBy` 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 getLabeledBy() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3318"
- column="40"/>
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3276"
- column="17"
- message="Setter here"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="This method should be called `getCanOpenPopup` such that `canOpenPopup` can be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
- errorLine1=" public boolean canOpenPopup() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3331"
- column="20"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="The getter return type (`AccessibilityNodeInfoCompat`) and setter parameter type (`View`) getter and setter methods for property `traversalBefore` 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 getTraversalBefore() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3533"
- column="40"/>
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3555"
- column="17"
- message="Setter here"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="The getter return type (`AccessibilityNodeInfoCompat`) and setter parameter type (`View`) getter and setter methods for property `traversalAfter` 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 getTraversalAfter() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3598"
- column="40"/>
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3620"
- column="17"
- message="Setter here"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="The getter return type (`AccessibilityNodeInfoCompat`) and setter parameter type (`View`) getter and setter methods for property `source` 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 getSource() {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="159"
- column="40"/>
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="97"
- column="17"
- message="Setter here"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="This method should be called `setLongpressEnabled` such that (along with the `isLongpressEnabled` getter) Kotlin code can access it as a property (`longpressEnabled`); see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
- errorLine1=" public void setIsLongpressEnabled(boolean enabled) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/GestureDetectorCompat.java"
- line="555"
- column="17"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="This method should be called `getShouldShowIcon` such that `shouldShowIcon` can be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
- errorLine1=" public boolean shouldShowIcon() {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteActionCompat.java"
- line="150"
- column="20"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="This method should be called `getMipMap` such that `mipMap` can be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
- errorLine1=" public boolean hasMipMap() {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/RoundedBitmapDrawable.java"
- line="181"
- column="20"/>
- </issue>
-
- <issue
- id="KotlinPropertyAccess"
- message="This method should be called `getAntiAlias` such that `antiAlias` can be accessed as a property from Kotlin; see https://android.github.io/kotlin-guides/interop.html#property-prefixes"
- errorLine1=" public boolean hasAntiAlias() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/RoundedBitmapDrawable.java"
- line="205"
- column="20"/>
- </issue>
-
- <issue
- id="LambdaLast"
- message="Functional interface parameters (such as parameter 2, "r", in androidx.core.os.HandlerCompat.postDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
- errorLine1=" @Nullable Object token, long delayMillis) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/os/HandlerCompat.java"
- line="181"
- column="37"/>
- </issue>
-
- <issue
- id="LambdaLast"
- message="Functional interface parameters (such as parameter 1, "component", in androidx.core.view.KeyEventDispatcher.dispatchKeyEvent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
- errorLine1=" @Nullable View root, @Nullable Window.Callback callback, @NonNull KeyEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/KeyEventDispatcher.java"
- line="79"
- column="70"/>
- </issue>
-
- <issue
- id="LambdaLast"
- message="Functional interface parameters (such as parameter 1, "callable", in androidx.core.provider.SelfDestructiveThread.postAndWait) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
- errorLine1=" public <T> T postAndWait(final Callable<T> callable, int timeoutMillis)"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/SelfDestructiveThread.java"
- line="169"
- column="58"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" AccessibilityNodeInfoCompat nodeInfoCompat, int clickableSpanActionId) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityClickableSpanCompat.java"
- line="55"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityDelegateCompat(AccessibilityDelegate originalDelegate) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="147"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void sendAccessibilityEvent(View host, int eventType) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="173"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="195"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="195"
- column="60"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="216"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="216"
- column="66"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="236"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="236"
- column="57"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="256"
- column="48"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="256"
- column="59"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="275"
- column="51"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="275"
- column="62"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="299"
- column="52"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="299"
- column="68"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="300"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="318"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="318"
- column="73"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean performAccessibilityAction(View host, int action, Bundle args) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="344"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean performAccessibilityAction(View host, int action, Bundle args) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/AccessibilityDelegateCompat.java"
- line="344"
- column="70"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getRecordCount(AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="257"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void appendRecord(AccessibilityEvent event, AccessibilityRecordCompat record) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="273"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void appendRecord(AccessibilityEvent event, AccessibilityRecordCompat record) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="273"
- column="63"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityRecordCompat getRecord(AccessibilityEvent event, int index) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="287"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityRecordCompat getRecord(AccessibilityEvent event, int index) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="287"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityRecordCompat asRecord(AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="308"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityRecordCompat asRecord(AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="308"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setContentChangeTypes(AccessibilityEvent event,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="320"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getContentChangeTypes(AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="342"
- column="45"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setMovementGranularity(AccessibilityEvent event, int granularity) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="357"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getMovementGranularity(AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="368"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setAction(AccessibilityEvent event, int action) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="393"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getAction(AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityEventCompat.java"
- line="404"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean addAccessibilityStateChangeListener(AccessibilityManager manager,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="45"
- column="63"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" AccessibilityStateChangeListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="46"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean removeAccessibilityStateChangeListener(AccessibilityManager manager,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="66"
- column="66"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" AccessibilityStateChangeListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="67"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="118"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" AccessibilityManager manager) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="119"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="141"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" AccessibilityManager manager, int feedbackTypeFlags) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="142"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean isTouchExplorationEnabled(AccessibilityManager manager) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="155"
- column="53"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean addTouchExplorationStateChangeListener(AccessibilityManager manager,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="166"
- column="66"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" TouchExplorationStateChangeListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="167"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean removeTouchExplorationStateChangeListener(AccessibilityManager manager,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="185"
- column="69"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" TouchExplorationStateChangeListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityManagerCompat.java"
- line="186"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityActionCompat(int actionId, CharSequence label) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="608"
- column="56"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityActionCompat(int actionId, CharSequence label,"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="621"
- column="56"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" AccessibilityViewCommand command) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="622"
- column="17"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getLabel() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="667"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean perform(View view, Bundle arguments) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="683"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean perform(View view, Bundle arguments) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="683"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityActionCompat createReplacementAction(CharSequence label,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="707"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityActionCompat createReplacementAction(CharSequence label,"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="707"
- column="66"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" AccessibilityViewCommand command) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="708"
- column="17"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static CollectionInfoCompat obtain(int rowCount, int columnCount,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="777"
- column="23"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static CollectionInfoCompat obtain(int rowCount, int columnCount,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="799"
- column="23"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static CollectionItemInfoCompat obtain(int rowIndex, int rowSpan,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="898"
- column="23"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static CollectionItemInfoCompat obtain(int rowIndex, int rowSpan,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="923"
- column="23"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static RangeInfoCompat obtain(int type, float min, float max, float current) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1040"
- column="23"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat(Object info) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1696"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityNodeInfoCompat wrap(@NonNull AccessibilityNodeInfo info) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1710"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfo unwrap() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1717"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Object getInfo() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1727"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityNodeInfoCompat obtain(View source) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1738"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityNodeInfoCompat obtain(View source) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1738"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityNodeInfoCompat obtain(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1752"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityNodeInfoCompat obtain(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1752"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityNodeInfoCompat obtain() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1766"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityNodeInfoCompat obtain(AccessibilityNodeInfoCompat info) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1777"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityNodeInfoCompat obtain(AccessibilityNodeInfoCompat info) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1777"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setSource(View source) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1786"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setSource(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1812"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat findFocus(int focus) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1832"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat focusSearch(int direction) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1854"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat getChild(int index) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1893"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addChild(View child) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1908"
- column="26"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addChild(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1926"
- column="26"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean removeChild(View child) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1946"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean removeChild(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="1964"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addAction(AccessibilityActionCompat action) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2029"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean removeAction(AccessibilityActionCompat action) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2054"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean performAction(int action, Bundle arguments) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2091"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2143"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2143"
- column="79"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat getParent() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2164"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setParent(View parent) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2179"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setParent(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2205"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void getBoundsInParent(Rect outBounds) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2226"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setBoundsInParent(Rect bounds) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2249"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void getBoundsInScreen(Rect outBounds) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2258"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setBoundsInScreen(Rect bounds) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2273"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getPackageName() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2617"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setPackageName(CharSequence packageName) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2632"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getClassName() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2641"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setClassName(CharSequence className) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2656"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getText() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2665"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setText(CharSequence text) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2695"
- column="25"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addSpansToExtras(CharSequence text, View view) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2703"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addSpansToExtras(CharSequence text, View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2703"
- column="53"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ClickableSpan[] getClickableSpans(CharSequence text) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2740"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ClickableSpan[] getClickableSpans(CharSequence text) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2740"
- column="53"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getContentDescription() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2801"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setContentDescription(CharSequence contentDescription) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2831"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setViewIdResourceName(String viewId) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2876"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String getViewIdResourceName() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2894"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CollectionInfoCompat getCollectionInfo() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2988"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setCollectionInfo(Object collectionInfo) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="2998"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setCollectionItemInfo(Object collectionItemInfo) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3007"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CollectionItemInfoCompat getCollectionItemInfo() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3021"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RangeInfoCompat getRangeInfo() {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3036"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setRangeInfo(RangeInfoCompat rangeInfo) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3056"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public List<AccessibilityActionCompat> getActionList() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3073"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setError(CharSequence error) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3200"
- column="26"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getError() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3211"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setLabelFor(View labeled) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3225"
- column="29"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setLabelFor(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3245"
- column="29"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat getLabelFor() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3262"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setLabeledBy(View label) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3276"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setLabeledBy(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3301"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat getLabeledBy() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3318"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(String viewId) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3378"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByViewId(String viewId) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3378"
- column="81"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Bundle getExtras() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3404"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat getTraversalBefore() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3533"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setTraversalBefore(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3555"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setTraversalBefore(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3582"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat getTraversalAfter() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3598"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setTraversalAfter(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3620"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setTraversalAfter(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3646"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityWindowInfoCompat getWindow() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeInfoCompat.java"
- line="3659"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeProviderCompat(Object provider) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeProviderCompat.java"
- line="137"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Object getProvider() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeProviderCompat.java"
- line="144"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean performAction(int virtualViewId, int action, Bundle arguments) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeProviderCompat.java"
- line="188"
- column="65"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public List<AccessibilityNodeInfoCompat> findAccessibilityNodeInfosByText(String text,"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityNodeProviderCompat.java"
- line="207"
- column="79"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityRecordCompat(Object record) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="43"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Object getImpl() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="54"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityRecordCompat obtain(AccessibilityRecordCompat record) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="69"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityRecordCompat obtain(AccessibilityRecordCompat record) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="69"
- column="52"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityRecordCompat obtain() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="83"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setSource(View source) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="97"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setSource(View root, int virtualDescendantId) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="118"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setSource(@NonNull AccessibilityRecord record, View root,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="137"
- column="71"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat getSource() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="159"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getMaxScrollX(AccessibilityRecord record) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="481"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setMaxScrollX(AccessibilityRecord record, int maxScrollX) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="507"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getMaxScrollY(AccessibilityRecord record) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="531"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setMaxScrollY(AccessibilityRecord record, int maxScrollY) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="557"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getClassName() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="623"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setClassName(CharSequence className) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="637"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public List<CharSequence> getText() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="650"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getBeforeText() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="662"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setBeforeText(CharSequence beforeText) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="676"
- column="31"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getContentDescription() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="688"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setContentDescription(CharSequence contentDescription) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="702"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Parcelable getParcelableData() {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="714"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setParcelableData(Parcelable parcelableData) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityRecordCompat.java"
- line="728"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setBundle(Bundle bundle) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityViewCommand.java"
- line="53"
- column="31"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String getHTMLElement() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityViewCommand.java"
- line="103"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getText() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityViewCommand.java"
- line="138"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat getRoot() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"
- line="127"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityWindowInfoCompat getParent() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"
- line="141"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void getBoundsInScreen(Rect outBounds) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"
- line="167"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityWindowInfoCompat getChild(int index) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"
- line="233"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getTitle() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"
- line="247"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AccessibilityNodeInfoCompat getAnchor() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"
- line="260"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityWindowInfoCompat obtain() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"
- line="275"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityWindowInfoCompat obtain(AccessibilityWindowInfoCompat info) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"
- line="291"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static AccessibilityWindowInfoCompat obtain(AccessibilityWindowInfoCompat info) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/accessibility/AccessibilityWindowInfoCompat.java"
- line="291"
- column="56"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ActionProvider(Context context) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ActionProvider.java"
- line="133"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Context getContext() {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ActionProvider.java"
- line="140"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public abstract View onCreateActionView();"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ActionProvider.java"
- line="149"
- column="21"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public View onCreateActionView(MenuItem forItem) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ActionProvider.java"
- line="163"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public View onCreateActionView(MenuItem forItem) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ActionProvider.java"
- line="163"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onPrepareSubMenu(SubMenu subMenu) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ActionProvider.java"
- line="262"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setSubUiVisibilityListener(SubUiVisibilityListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ActionProvider.java"
- line="282"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setVisibilityListener(VisibilityListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ActionProvider.java"
- line="292"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PermissionCompatDelegate getPermissionCompatDelegate() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/ActivityCompat.java"
- line="173"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean invalidateOptionsMenu(Activity activity) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/ActivityCompat.java"
- line="211"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static DragAndDropPermissionsCompat requestDragAndDropPermissions(Activity activity,"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/ActivityCompat.java"
- line="605"
- column="78"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" DragEvent dragEvent) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/ActivityCompat.java"
- line="606"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Pair<View, String>... sharedElements) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/ActivityOptionsCompat.java"
- line="206"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AutoScrollHelper setEnabled(boolean enabled) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/AutoScrollHelper.java"
- line="235"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AutoScrollHelper setExclusive(boolean exclusive) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/AutoScrollHelper.java"
- line="263"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onTouch(View v, MotionEvent event) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/AutoScrollHelper.java"
- line="465"
- column="28"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onTouch(View v, MotionEvent event) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/AutoScrollHelper.java"
- line="465"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" int[] getAutoSizeTextAvailableSizes();"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/widget/AutoSizeableTextView.java"
- line="151"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Builder(Locale locale) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="157"
- column="24"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Builder stereoReset(boolean stereoReset) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="176"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Builder setTextDirectionHeuristic(TextDirectionHeuristicCompat heuristic) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="192"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Builder setTextDirectionHeuristic(TextDirectionHeuristicCompat heuristic) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="192"
- column="50"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public BidiFormatter build() {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="204"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static BidiFormatter getInstance() {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="235"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static BidiFormatter getInstance(boolean rtlContext) {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="244"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static BidiFormatter getInstance(Locale locale) {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="253"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static BidiFormatter getInstance(Locale locale) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="253"
- column="45"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean isRtl(String str) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="346"
- column="26"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean isRtl(CharSequence str) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="357"
- column="26"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String unicodeWrap(String str, TextDirectionHeuristicCompat heuristic, boolean isolate) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="390"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String unicodeWrap(String str, TextDirectionHeuristicCompat heuristic, boolean isolate) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="390"
- column="31"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String unicodeWrap(String str, TextDirectionHeuristicCompat heuristic, boolean isolate) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="390"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristicCompat heuristic,"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="409"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristicCompat heuristic,"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="409"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristicCompat heuristic,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="409"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String unicodeWrap(String str, TextDirectionHeuristicCompat heuristic) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="440"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String unicodeWrap(String str, TextDirectionHeuristicCompat heuristic) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="440"
- column="31"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String unicodeWrap(String str, TextDirectionHeuristicCompat heuristic) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="440"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristicCompat heuristic) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="455"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristicCompat heuristic) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="455"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence unicodeWrap(CharSequence str, TextDirectionHeuristicCompat heuristic) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="455"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String unicodeWrap(String str, boolean isolate) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="468"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String unicodeWrap(String str, boolean isolate) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="468"
- column="31"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence unicodeWrap(CharSequence str, boolean isolate) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="482"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence unicodeWrap(CharSequence str, boolean isolate) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="482"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String unicodeWrap(String str) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="493"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String unicodeWrap(String str) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="493"
- column="31"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence unicodeWrap(CharSequence str) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="505"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence unicodeWrap(CharSequence str) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/BidiFormatter.java"
- line="505"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onStateChanged(int[] stateSet) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/content/res/ComplexColorCompat.java"
- line="107"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void putExtraData(ExtraData extraData) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/ComponentActivity.java"
- line="78"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public <T extends ExtraData> T getExtraData(Class<T> extraDataClass) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/ComponentActivity.java"
- line="106"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean superDispatchKeyEvent(KeyEvent event) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/ComponentActivity.java"
- line="121"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean dispatchKeyShortcutEvent(KeyEvent event) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/ComponentActivity.java"
- line="126"
- column="45"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean dispatchKeyEvent(KeyEvent event) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/ComponentActivity.java"
- line="135"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Cursor query(ContentResolver resolver,"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/content/ContentResolverCompat.java"
- line="71"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Cursor query(ContentResolver resolver,"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/ContentResolverCompat.java"
- line="71"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Uri uri, String[] projection, String selection, String[] selectionArgs,"
- errorLine2=" ~~~">
- <location
- file="src/main/java/androidx/core/content/ContentResolverCompat.java"
- line="72"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Uri uri, String[] projection, String selection, String[] selectionArgs,"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/ContentResolverCompat.java"
- line="72"
- column="22"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Uri uri, String[] projection, String selection, String[] selectionArgs,"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/content/ContentResolverCompat.java"
- line="72"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Uri uri, String[] projection, String selection, String[] selectionArgs,"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/ContentResolverCompat.java"
- line="72"
- column="61"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" String sortOrder, CancellationSignal cancellationSignal) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/content/ContentResolverCompat.java"
- line="73"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" String sortOrder, CancellationSignal cancellationSignal) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/ContentResolverCompat.java"
- line="73"
- column="31"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Object getWrapper();"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/CoreComponentFactory.java"
- line="118"
- column="9"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static String concatenateWhere(String a, String b) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/database/DatabaseUtilsCompat.java"
- line="40"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static String concatenateWhere(String a, String b) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/database/DatabaseUtilsCompat.java"
- line="40"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static String concatenateWhere(String a, String b) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/database/DatabaseUtilsCompat.java"
- line="40"
- column="53"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/database/DatabaseUtilsCompat.java"
- line="59"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/database/DatabaseUtilsCompat.java"
- line="59"
- column="48"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/database/DatabaseUtilsCompat.java"
- line="59"
- column="73"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void buildShortClassTag(Object cls, StringBuilder out) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/util/DebugUtils.java"
- line="31"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void buildShortClassTag(Object cls, StringBuilder out) {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/util/DebugUtils.java"
- line="31"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public DisplayCutoutCompat(Rect safeInsets, List<Rect> boundingRects) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/DisplayCutoutCompat.java"
- line="55"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public DisplayCutoutCompat(Rect safeInsets, List<Rect> boundingRects) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/DisplayCutoutCompat.java"
- line="55"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static DragAndDropPermissionsCompat request(Activity activity, DragEvent dragEvent) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/DragAndDropPermissionsCompat.java"
- line="49"
- column="56"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static DragAndDropPermissionsCompat request(Activity activity, DragEvent dragEvent) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/DragAndDropPermissionsCompat.java"
- line="49"
- column="75"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" boolean onDragStart(View v, DragStartHelper helper);"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/DragStartHelper.java"
- line="87"
- column="29"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" boolean onDragStart(View v, DragStartHelper helper);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/DragStartHelper.java"
- line="87"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public DragStartHelper(View view, OnDragStartListener listener) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/DragStartHelper.java"
- line="96"
- column="28"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public DragStartHelper(View view, OnDragStartListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/DragStartHelper.java"
- line="96"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onTouch(View v, MotionEvent event) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/DragStartHelper.java"
- line="128"
- column="28"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onTouch(View v, MotionEvent event) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/DragStartHelper.java"
- line="128"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onLongClick(View v) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/DragStartHelper.java"
- line="169"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void getTouchPosition(Point point) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/view/DragStartHelper.java"
- line="177"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ColorFilter getColorFilter(@NonNull Drawable drawable) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"
- line="209"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Drawable wrap(@NonNull Drawable drawable) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/DrawableCompat.java"
- line="306"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public EdgeEffectCompat(Context context) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/EdgeEffectCompat.java"
- line="53"
- column="29"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean draw(Canvas canvas) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/EdgeEffectCompat.java"
- line="294"
- column="25"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Uri getUriForFile(@NonNull Context context, @NonNull String authority,"
- errorLine2=" ~~~">
- <location
- file="src/main/java/androidx/core/content/FileProvider.java"
- line="439"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/content/FileProvider.java"
- line="504"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String getType(@NonNull Uri uri) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/content/FileProvider.java"
- line="546"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Uri insert(@NonNull Uri uri, ContentValues values) {"
- errorLine2=" ~~~">
- <location
- file="src/main/java/androidx/core/content/FileProvider.java"
- line="567"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Uri insert(@NonNull Uri uri, ContentValues values) {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/FileProvider.java"
- line="567"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public int update(@NonNull Uri uri, ContentValues values, @Nullable String selection,"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/FileProvider.java"
- line="576"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public AuthenticationResult(CryptoObject crypto) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/hardware/fingerprint/FingerprintManagerCompat.java"
- line="251"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CryptoObject getCryptoObject() { return mCryptoObject; }"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/hardware/fingerprint/FingerprintManagerCompat.java"
- line="260"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onAuthenticationError(int errMsgId, CharSequence errString) { }"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/hardware/fingerprint/FingerprintManagerCompat.java"
- line="277"
- column="57"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { }"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/hardware/fingerprint/FingerprintManagerCompat.java"
- line="286"
- column="57"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onAuthenticationSucceeded(AuthenticationResult result) { }"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/hardware/fingerprint/FingerprintManagerCompat.java"
- line="292"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String getIdentifier() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/FontRequest.java"
- line="156"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static @Nullable FamilyResourceEntry parse(XmlPullParser parser, Resources resources)"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/res/FontResourcesParserCompat.java"
- line="181"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static @Nullable FamilyResourceEntry parse(XmlPullParser parser, Resources resources)"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/res/FontResourcesParserCompat.java"
- line="181"
- column="77"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static List<List<byte[]>> readCerts(Resources resources, @ArrayRes int certsId) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/res/FontResourcesParserCompat.java"
- line="269"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static List<List<byte[]>> readCerts(Resources resources, @ArrayRes int certsId) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/res/FontResourcesParserCompat.java"
- line="269"
- column="48"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public FontInfo[] getFonts() {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/FontsContractCompat.java"
- line="399"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onTypefaceRetrieved(Typeface typeface) {}"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/FontsContractCompat.java"
- line="486"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Typeface getFontSync("
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/FontsContractCompat.java"
- line="543"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" final Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/FontsContractCompat.java"
- line="544"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" final FontRequest request,"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/FontsContractCompat.java"
- line="545"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Map<Uri, ByteBuffer> prepareFontData("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/FontsContractCompat.java"
- line="588"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/FontsContractCompat.java"
- line="589"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" FontInfo[] fonts,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/FontsContractCompat.java"
- line="590"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" CancellationSignal cancellationSignal"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/FontsContractCompat.java"
- line="591"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public GestureDetectorCompat(Context context, OnGestureListener listener) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/GestureDetectorCompat.java"
- line="505"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public GestureDetectorCompat(Context context, OnGestureListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/GestureDetectorCompat.java"
- line="505"
- column="51"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public GestureDetectorCompat(Context context, OnGestureListener listener, Handler handler) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/GestureDetectorCompat.java"
- line="519"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public GestureDetectorCompat(Context context, OnGestureListener listener, Handler handler) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/GestureDetectorCompat.java"
- line="519"
- column="51"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public GestureDetectorCompat(Context context, OnGestureListener listener, Handler handler) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/GestureDetectorCompat.java"
- line="519"
- column="79"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onTouchEvent(MotionEvent event) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/GestureDetectorCompat.java"
- line="542"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setOnDoubleTapListener(OnDoubleTapListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/GestureDetectorCompat.java"
- line="566"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void apply(int gravity, int w, int h, Rect container,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/GravityCompat.java"
- line="62"
- column="57"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Rect outRect, int layoutDirection) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/GravityCompat.java"
- line="63"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void apply(int gravity, int w, int h, Rect container,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/GravityCompat.java"
- line="96"
- column="57"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" int xAdj, int yAdj, Rect outRect, int layoutDirection) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/GravityCompat.java"
- line="97"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/GravityCompat.java"
- line="125"
- column="50"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/GravityCompat.java"
- line="125"
- column="64"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static String maximizeAndGetScript(Locale locale) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/ICUCompat.java"
- line="88"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public byte[] mData = null;"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="176"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Parcelable mParcelable = null;"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="182"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ColorStateList mTintList = null;"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="206"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String mTintModeStr = null;"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="216"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String mString1;"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="223"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithResource(Context context, @DrawableRes int resId) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="232"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithResource(Context context, @DrawableRes int resId) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="232"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithResource(Resources r, String pkg, @DrawableRes int resId) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="243"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithResource(Resources r, String pkg, @DrawableRes int resId) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="243"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithResource(Resources r, String pkg, @DrawableRes int resId) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="243"
- column="62"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithBitmap(Bitmap bits) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="270"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithBitmap(Bitmap bits) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="270"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithAdaptiveBitmap(Bitmap bits) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="285"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithAdaptiveBitmap(Bitmap bits) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="285"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithData(byte[] data, int offset, int length) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="303"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithData(byte[] data, int offset, int length) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="303"
- column="45"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithContentUri(String uri) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="320"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithContentUri(String uri) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="320"
- column="51"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithContentUri(Uri uri) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="335"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static IconCompat createWithContentUri(Uri uri) {"
- errorLine2=" ~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="335"
- column="51"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public IconCompat setTint(@ColorInt int tint) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="496"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public IconCompat setTintList(ColorStateList tintList) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="506"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public IconCompat setTintList(ColorStateList tintList) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="506"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public IconCompat setTintMode(PorterDuff.Mode mode) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="517"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public IconCompat setTintMode(PorterDuff.Mode mode) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/IconCompat.java"
- line="517"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public IBinder onBind(@NonNull Intent intent) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/JobIntentService.java"
- line="468"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" boolean superDispatchKeyEvent(KeyEvent event);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/KeyEventDispatcher.java"
- line="191"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static LayoutInflaterFactory getFactory(LayoutInflater inflater) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/LayoutInflaterCompat.java"
- line="169"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static LayoutInflaterFactory getFactory(LayoutInflater inflater) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/LayoutInflaterCompat.java"
- line="169"
- column="52"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" View onCreateView(View parent, String name, Context context, AttributeSet attrs);"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/LayoutInflaterFactory.java"
- line="46"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" View onCreateView(View parent, String name, Context context, AttributeSet attrs);"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/LayoutInflaterFactory.java"
- line="46"
- column="23"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" View onCreateView(View parent, String name, Context context, AttributeSet attrs);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/LayoutInflaterFactory.java"
- line="46"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" View onCreateView(View parent, String name, Context context, AttributeSet attrs);"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/LayoutInflaterFactory.java"
- line="46"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" View onCreateView(View parent, String name, Context context, AttributeSet attrs);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/LayoutInflaterFactory.java"
- line="46"
- column="66"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/ListPopupWindowCompat.java"
- line="64"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/ListPopupWindowCompat.java"
- line="64"
- column="60"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/ListPopupWindowCompat.java"
- line="64"
- column="84"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static LocaleListCompat wrap(Object localeList) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/os/LocaleListCompat.java"
- line="45"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static LocaleListCompat wrap(Object localeList) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/os/LocaleListCompat.java"
- line="45"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Locale get(int index) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/os/LocaleListCompat.java"
- line="85"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public int indexOf(Locale locale) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/os/LocaleListCompat.java"
- line="116"
- column="24"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public LogWriter(String tag) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/util/LogWriter.java"
- line="43"
- column="22"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" @Override public void write(char[] buf, int offset, int count) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/util/LogWriter.java"
- line="55"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getMarginStart(ViewGroup.MarginLayoutParams lp) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MarginLayoutParamsCompat.java"
- line="41"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getMarginEnd(ViewGroup.MarginLayoutParams lp) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MarginLayoutParamsCompat.java"
- line="60"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setMarginStart(ViewGroup.MarginLayoutParams lp, int marginStart) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MarginLayoutParamsCompat.java"
- line="79"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setMarginEnd(ViewGroup.MarginLayoutParams lp, int marginEnd) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MarginLayoutParamsCompat.java"
- line="98"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean isMarginRelative(ViewGroup.MarginLayoutParams lp) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MarginLayoutParamsCompat.java"
- line="111"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getLayoutDirection(ViewGroup.MarginLayoutParams lp) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MarginLayoutParamsCompat.java"
- line="125"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setLayoutDirection(ViewGroup.MarginLayoutParams lp, int layoutDirection) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MarginLayoutParamsCompat.java"
- line="149"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void resolveLayoutDirection(ViewGroup.MarginLayoutParams lp,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MarginLayoutParamsCompat.java"
- line="159"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setShowAsAction(MenuItem item, int actionEnum) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuCompat.java"
- line="35"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setGroupDividerEnabled(Menu menu, boolean enabled) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuCompat.java"
- line="45"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" boolean onMenuItemActionExpand(MenuItem item);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="109"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" boolean onMenuItemActionCollapse(MenuItem item);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="118"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setShowAsAction(MenuItem item, int actionEnum) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="134"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static MenuItem setActionView(MenuItem item, View view) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="152"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static MenuItem setActionView(MenuItem item, View view) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="152"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static MenuItem setActionView(MenuItem item, View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="152"
- column="57"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static MenuItem setActionView(MenuItem item, int resId) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="174"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static MenuItem setActionView(MenuItem item, int resId) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="174"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static View getActionView(MenuItem item) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="187"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static View getActionView(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="187"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static MenuItem setActionProvider(MenuItem item, ActionProvider provider) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="206"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static MenuItem setActionProvider(MenuItem item, ActionProvider provider) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="206"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static MenuItem setActionProvider(MenuItem item, ActionProvider provider) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="206"
- column="61"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ActionProvider getActionProvider(MenuItem item) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="223"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ActionProvider getActionProvider(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="223"
- column="52"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean expandActionView(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="248"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean collapseActionView(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="267"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean isActionViewExpanded(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="283"
- column="48"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static MenuItem setOnActionExpandListener(MenuItem item,"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="300"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static MenuItem setOnActionExpandListener(MenuItem item,"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="300"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" final OnActionExpandListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="301"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setContentDescription(MenuItem item, CharSequence contentDescription) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="321"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setContentDescription(MenuItem item, CharSequence contentDescription) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="321"
- column="61"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static CharSequence getContentDescription(MenuItem item) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="334"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static CharSequence getContentDescription(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="334"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setTooltipText(MenuItem item, CharSequence tooltipText) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="350"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setTooltipText(MenuItem item, CharSequence tooltipText) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="350"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static CharSequence getTooltipText(MenuItem item) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="363"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static CharSequence getTooltipText(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="363"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setShortcut(MenuItem item, char numericChar, char alphaChar,"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="395"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="417"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getNumericModifiers(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="435"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="462"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getAlphabeticModifiers(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="480"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setIconTintList(MenuItem item, ColorStateList tint) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="502"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setIconTintList(MenuItem item, ColorStateList tint) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="502"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ColorStateList getIconTintList(MenuItem item) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="514"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ColorStateList getIconTintList(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="514"
- column="50"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="533"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setIconTintMode(MenuItem item, PorterDuff.Mode tintMode) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="533"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PorterDuff.Mode getIconTintMode(MenuItem item) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="547"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PorterDuff.Mode getIconTintMode(MenuItem item) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MenuItemCompat.java"
- line="547"
- column="51"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getActionMasked(MotionEvent event) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="464"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getActionIndex(MotionEvent event) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="476"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int findPointerIndex(MotionEvent event, int pointerId) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="487"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getPointerId(MotionEvent event, int pointerIndex) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="498"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getX(MotionEvent event, int pointerIndex) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="509"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getY(MotionEvent event, int pointerIndex) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="520"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getPointerCount(MotionEvent event) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="531"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getSource(MotionEvent event) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="543"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean isFromSource(MotionEvent event, int source) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="552"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getAxisValue(MotionEvent event, int axis) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="569"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getAxisValue(MotionEvent event, int axis, int pointerIndex) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="589"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getButtonState(MotionEvent event) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/MotionEventCompat.java"
- line="598"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" void onScrollChange(NestedScrollView v, int scrollX, int scrollY,"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="99"
- column="29"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" int dyUnconsumed, int[] offsetInWindow, int type) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="262"
- column="31"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="268"
- column="60"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="268"
- column="76"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" int dyUnconsumed, int[] offsetInWindow) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="302"
- column="31"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="308"
- column="60"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="308"
- column="76"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addView(View child) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="487"
- column="25"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addView(View child, int index) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="496"
- column="25"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addView(View child, ViewGroup.LayoutParams params) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="505"
- column="25"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addView(View child, ViewGroup.LayoutParams params) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="505"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addView(View child, int index, ViewGroup.LayoutParams params) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="514"
- column="25"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void addView(View child, int index, ViewGroup.LayoutParams params) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="514"
- column="48"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean dispatchKeyEvent(KeyEvent event) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="635"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onInterceptTouchEvent(MotionEvent ev) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="732"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onTouchEvent(MotionEvent ev) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="844"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean onGenericMotionEvent(MotionEvent event) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="1067"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected void measureChild(View child, int parentWidthMeasureSpec,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="1630"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="1646"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="1814"
- column="60"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void requestChildFocus(View child, View focused) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="1880"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void requestChildFocus(View child, View focused) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="1880"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Rect previouslyFocusedRect) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="1900"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean requestChildRectangleOnScreen(View child, Rect rectangle,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="1927"
- column="50"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public boolean requestChildRectangleOnScreen(View child, Rect rectangle,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="1927"
- column="62"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void draw(Canvas canvas) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="2072"
- column="22"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected void onRestoreInstanceState(Parcelable state) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="2151"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Parcelable onSaveInstanceState() {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/NestedScrollView.java"
- line="2164"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Notification.Builder getBuilder();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationBuilderWithBuilderAccessor.java"
- line="34"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Context mContext;"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="901"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ArrayList<Action> mActions = new ArrayList<>();"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="905"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ArrayList<String> mPeople;"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="967"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews getContentView() {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2454"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews getBigContentView() {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2462"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews getHeadsUpContentView() {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2470"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Builder mBuilder;"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2538"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void apply(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2584"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2600"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2600"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2608"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2608"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2616"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2616"
- column="51"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Bitmap createColoredBitmap(int iconId, int color) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2927"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void buildIntoRemoteViews(RemoteViews outerView,"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2977"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" RemoteViews innerView) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="2978"
- column="17"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void apply(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="3127"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void apply(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="3357"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void apply(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="3701"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void apply(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4261"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void apply(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4362"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4373"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4373"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4390"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4390"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4411"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor builder) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4411"
- column="51"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence title;"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4595"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public PendingIntent actionIntent;"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompat.java"
- line="4600"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public IBinder onBind(Intent intent) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompatSideChannelService.java"
- line="47"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public IBinder onBind(Intent intent) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompatSideChannelService.java"
- line="47"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public abstract void notify(String packageName, int id, String tag, Notification notification);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompatSideChannelService.java"
- line="62"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public abstract void notify(String packageName, int id, String tag, Notification notification);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompatSideChannelService.java"
- line="62"
- column="61"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public abstract void notify(String packageName, int id, String tag, Notification notification);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompatSideChannelService.java"
- line="62"
- column="73"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public abstract void cancel(String packageName, int id, String tag);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompatSideChannelService.java"
- line="67"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public abstract void cancel(String packageName, int id, String tag);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompatSideChannelService.java"
- line="67"
- column="61"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public abstract void cancelAll(String packageName);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/NotificationCompatSideChannelService.java"
- line="72"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/OnApplyWindowInsetsListener.java"
- line="42"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets);"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/OnApplyWindowInsetsListener.java"
- line="42"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/OnApplyWindowInsetsListener.java"
- line="42"
- column="52"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onViewAttachedToWindow(View v) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/OneShotPreDrawListener.java"
- line="89"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onViewDetachedFromWindow(View v) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/OneShotPreDrawListener.java"
- line="94"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static <T> Parcelable.Creator<T> newCreator("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/os/ParcelableCompat.java"
- line="39"
- column="23"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" ParcelableCompatCreatorCallbacks<T> callbacks) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/os/ParcelableCompat.java"
- line="40"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" T createFromParcel(Parcel in, ClassLoader loader);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/os/ParcelableCompatCreatorCallbacks.java"
- line="40"
- column="24"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" T createFromParcel(Parcel in, ClassLoader loader);"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/os/ParcelableCompatCreatorCallbacks.java"
- line="40"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" T[] newArray(int size);"
- errorLine2=" ~~~">
- <location
- file="src/main/java/androidx/core/os/ParcelableCompatCreatorCallbacks.java"
- line="49"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Interpolator create(Path path) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java"
- line="47"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Interpolator create(Path path) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java"
- line="47"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Interpolator create(float controlX, float controlY) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java"
- line="62"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Interpolator create(float controlX1, float controlY1,"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java"
- line="79"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Path createPathFromPathData(String pathData) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="72"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Path createPathFromPathData(String pathData) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="72"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PathDataNode[] createNodesFromPathData(String pathData) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="90"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PathDataNode[] createNodesFromPathData(String pathData) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="90"
- column="58"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="119"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="119"
- column="48"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="162"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="162"
- column="59"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean interpolatePathDataNodes(PathDataNode[] target, PathDataNode[] from,"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="313"
- column="52"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean interpolatePathDataNodes(PathDataNode[] target, PathDataNode[] from,"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="313"
- column="75"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" PathDataNode[] to, float fraction) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="314"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public float[] mParams;"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="350"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void nodesToPath(PathDataNode[] node, Path path) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="368"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void nodesToPath(PathDataNode[] node, Path path) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="368"
- column="61"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void interpolatePathDataNode(PathDataNode nodeFrom, PathDataNode nodeTo,"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="386"
- column="45"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void interpolatePathDataNode(PathDataNode nodeFrom, PathDataNode nodeTo,"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/PathParser.java"
- line="386"
- column="68"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Object getPointerIcon() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/PointerIconCompat.java"
- line="115"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PointerIconCompat getSystemIcon(Context context, int style) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/PointerIconCompat.java"
- line="129"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PointerIconCompat getSystemIcon(Context context, int style) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/PointerIconCompat.java"
- line="129"
- column="51"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PointerIconCompat create(Bitmap bitmap, float hotSpotX, float hotSpotY) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/PointerIconCompat.java"
- line="150"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PointerIconCompat create(Bitmap bitmap, float hotSpotX, float hotSpotY) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/PointerIconCompat.java"
- line="150"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PointerIconCompat load(Resources resources, int resourceId) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/PointerIconCompat.java"
- line="179"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PointerIconCompat load(Resources resources, int resourceId) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/PointerIconCompat.java"
- line="179"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Builder setBreakStrategy(int strategy) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="143"
- column="20"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Builder setHyphenationFrequency(int frequency) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="161"
- column="20"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="179"
- column="20"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static PrecomputedTextCompat create(@NonNull CharSequence text, @NonNull Params params) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="428"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Future<PrecomputedTextCompat> getTextFuture("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="644"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setSpan(Object what, int start, int end, int flags) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="670"
- column="25"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void removeSpan(Object what) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="686"
- column="28"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public <T> T[] getSpans(int start, int end, Class<T> type) {"
- errorLine2=" ~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="704"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public <T> T[] getSpans(int start, int end, Class<T> type) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="704"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public int getSpanStart(Object tag) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="714"
- column="29"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public int getSpanEnd(Object tag) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="719"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public int getSpanFlags(Object tag) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="724"
- column="29"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public int nextSpanTransition(int start, int limit, Class type) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="729"
- column="57"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence subSequence(int start, int end) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/PrecomputedTextCompat.java"
- line="749"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public IconCompat mIcon;"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteActionCompat.java"
- line="47"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence mTitle;"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteActionCompat.java"
- line="53"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence mContentDescription;"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteActionCompat.java"
- line="59"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public PendingIntent mActionIntent;"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteActionCompat.java"
- line="65"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public String getResultKey() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="115"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence getLabel() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="122"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public CharSequence[] getChoices() {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="129"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Set<String> getAllowedDataTypes() {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="133"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Bundle getExtras() {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="170"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Map<String, Uri> getDataResultsFromIntent("
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="336"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Intent intent, String remoteInputResultKey) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="337"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Intent intent, String remoteInputResultKey) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="337"
- column="28"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Bundle getResultsFromIntent(Intent intent) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="375"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Bundle getResultsFromIntent(Intent intent) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="375"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="400"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void addResultsToIntent(RemoteInput[] remoteInputs, Intent intent,"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="400"
- column="71"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Bundle results) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="401"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void addDataResultToIntent(RemoteInput remoteInput, Intent intent,"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="464"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void addDataResultToIntent(RemoteInput remoteInput, Intent intent,"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="464"
- column="71"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Map<String, Uri> results) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/RemoteInput.java"
- line="465"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public final void callbackSuccessAsync(final Typeface typeface, @Nullable Handler handler) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
- line="437"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Typeface getFont(@NonNull Context context, @FontRes int id, TypedValue value,"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
- line="506"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Typeface getFont(@NonNull Context context, @FontRes int id, TypedValue value,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/res/ResourcesCompat.java"
- line="506"
- column="79"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ResultReceiver(Handler handler) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/android/support/v4/os/ResultReceiver.java"
- line="83"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void send(int resultCode, Bundle resultData) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/android/support/v4/os/ResultReceiver.java"
- line="95"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected void onReceiveResult(int resultCode, Bundle resultData) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/android/support/v4/os/ResultReceiver.java"
- line="120"
- column="52"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void writeToParcel(Parcel out, int flags) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/android/support/v4/os/ResultReceiver.java"
- line="129"
- column="31"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void setColorFilter(ColorFilter cf) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/RoundedBitmapDrawable.java"
- line="288"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ColorFilter getColorFilter() {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/RoundedBitmapDrawable.java"
- line="294"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected void onBoundsChange(Rect bounds) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/RoundedBitmapDrawable.java"
- line="344"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setQuickScaleEnabled(Object scaleGestureDetector, boolean enabled) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ScaleGestureDetectorCompat.java"
- line="39"
- column="45"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" ScaleGestureDetector scaleGestureDetector, boolean enabled) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ScaleGestureDetectorCompat.java"
- line="52"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean isQuickScaleEnabled(Object scaleGestureDetector) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ScaleGestureDetectorCompat.java"
- line="67"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean isQuickScaleEnabled(ScaleGestureDetector scaleGestureDetector) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ScaleGestureDetectorCompat.java"
- line="77"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ScrollerCompat create(Context context) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/ScrollerCompat.java"
- line="40"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ScrollerCompat create(Context context) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/ScrollerCompat.java"
- line="40"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ScrollerCompat create(Context context, Interpolator interpolator) {"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/ScrollerCompat.java"
- line="48"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ScrollerCompat create(Context context, Interpolator interpolator) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/ScrollerCompat.java"
- line="48"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ScrollerCompat create(Context context, Interpolator interpolator) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/widget/ScrollerCompat.java"
- line="48"
- column="58"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" String threadName, int priority, int destructAfterMillisec) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/SelfDestructiveThread.java"
- line="83"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public <T> void postAndReply(final Callable<T> callable, final ReplyCallback<T> reply) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/SelfDestructiveThread.java"
- line="140"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public <T> void postAndReply(final Callable<T> callable, final ReplyCallback<T> reply) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/SelfDestructiveThread.java"
- line="140"
- column="68"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public <T> T postAndWait(final Callable<T> callable, int timeoutMillis)"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/provider/SelfDestructiveThread.java"
- line="169"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onSharedElementStart(List<String> sharedElementNames,"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="77"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" List<View> sharedElements, List<View> sharedElementSnapshots) {}"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="78"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" List<View> sharedElements, List<View> sharedElementSnapshots) {}"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="78"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onSharedElementEnd(List<String> sharedElementNames,"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="108"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" List<View> sharedElements, List<View> sharedElementSnapshots) {}"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="109"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" List<View> sharedElements, List<View> sharedElementSnapshots) {}"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="109"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onRejectSharedElements(List<View> rejectedSharedElements) {}"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="133"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {}"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="144"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {}"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="144"
- column="57"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="166"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="166"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix,"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="166"
- column="74"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" RectF screenBounds) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="167"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public View onCreateSnapshotView(Context context, Parcelable snapshot) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="256"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public View onCreateSnapshotView(Context context, Parcelable snapshot) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="256"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public View onCreateSnapshotView(Context context, Parcelable snapshot) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="256"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onSharedElementsArrived(List<String> sharedElementNames,"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="299"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" List<View> sharedElements, OnSharedElementsReadyListener listener) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="300"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" List<View> sharedElements, OnSharedElementsReadyListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/SharedElementCallback.java"
- line="300"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static EditorCompat getInstance() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/SharedPreferencesCompat.java"
- line="65"
- column="23"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ShortcutInfo toShortcutInfo() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"
- line="107"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public IconCompat getIcon() {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"
- line="325"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Builder setIcon(IconCompat icon) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java"
- line="662"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public abstract T addShortcuts(List<ShortcutInfoCompat> shortcuts);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompatSaver.java"
- line="38"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public abstract T removeShortcuts(List<String> shortcutIds);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompatSaver.java"
- line="41"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public List<ShortcutInfoCompat> getShortcuts() throws Exception {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompatSaver.java"
- line="47"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Void addShortcuts(List<ShortcutInfoCompat> shortcuts) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompatSaver.java"
- line="59"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Void addShortcuts(List<ShortcutInfoCompat> shortcuts) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompatSaver.java"
- line="59"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Void removeShortcuts(List<String> shortcutIds) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompatSaver.java"
- line="64"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Void removeShortcuts(List<String> shortcutIds) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompatSaver.java"
- line="64"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Void removeAllShortcuts() {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/content/pm/ShortcutInfoCompatSaver.java"
- line="69"
- column="16"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setShowAsActionFlags(int actionEnum);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="107"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setActionView(View view);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="122"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setActionView(View view);"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="122"
- column="28"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setActionView(int resId);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="137"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" View getActionView();"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="147"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" SupportMenuItem setSupportActionProvider(ActionProvider actionProvider);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="162"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" SupportMenuItem setSupportActionProvider(ActionProvider actionProvider);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="162"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" ActionProvider getSupportActionProvider();"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="171"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" SupportMenuItem setContentDescription(CharSequence contentDescription);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="220"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" SupportMenuItem setContentDescription(CharSequence contentDescription);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="220"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" CharSequence getContentDescription();"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="228"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" SupportMenuItem setTooltipText(CharSequence tooltipText);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="237"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" SupportMenuItem setTooltipText(CharSequence tooltipText);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="237"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" CharSequence getTooltipText();"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="245"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="271"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setNumericShortcut(char numericChar, int numericModifiers);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="288"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="322"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setIconTintList(ColorStateList tint);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="350"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setIconTintList(ColorStateList tint);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="350"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" ColorStateList getIconTintList();"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="357"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setIconTintMode(PorterDuff.Mode tintMode);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="369"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" MenuItem setIconTintMode(PorterDuff.Mode tintMode);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="369"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" PorterDuff.Mode getIconTintMode();"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/internal/view/SupportMenuItem.java"
- line="378"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static TaskStackBuilder from(Context context) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/TaskStackBuilder.java"
- line="108"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static TaskStackBuilder from(Context context) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/TaskStackBuilder.java"
- line="108"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public TaskStackBuilder addParentStack(ComponentName sourceActivityName) {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/TaskStackBuilder.java"
- line="200"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public TaskStackBuilder addParentStack(ComponentName sourceActivityName) {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/TaskStackBuilder.java"
- line="200"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Intent getIntent(int index) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/app/TaskStackBuilder.java"
- line="233"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Iterator<Intent> iterator() {"
- errorLine2=" ~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/app/TaskStackBuilder.java"
- line="255"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" boolean isRtl(char[] array, int start, int count);"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/text/TextDirectionHeuristicCompat.java"
- line="33"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" boolean isRtl(CharSequence cs, int start, int count);"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/text/TextDirectionHeuristicCompat.java"
- line="45"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void formatDuration(long duration, StringBuilder builder) {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/util/TimeUtils.java"
- line="153"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/util/TimeUtils.java"
- line="162"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void formatDuration(long duration, PrintWriter pw) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/util/TimeUtils.java"
- line="171"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void formatDuration(long time, long now, PrintWriter pw) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/util/TimeUtils.java"
- line="177"
- column="60"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" void setTintList(ColorStateList tint);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/TintAwareDrawable.java"
- line="36"
- column="22"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" void setTintMode(PorterDuff.Mode tintMode);"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/TintAwareDrawable.java"
- line="37"
- column="22"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void tagSocket(Socket socket) throws SocketException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/net/TrafficStatsCompat.java"
- line="113"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void untagSocket(Socket socket) throws SocketException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/net/TrafficStatsCompat.java"
- line="123"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ComplexColorCompat getNamedComplexColor(@NonNull TypedArray a,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/content/res/TypedArrayUtils.java"
- line="137"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" @NonNull Context context, @NonNull Resources resources, int id, String path,"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompat.java"
- line="172"
- column="77"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Typeface createFromFamiliesWithDefault(Object family) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="167"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Typeface createFromFontFamilyFilesResourceEntry(Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="200"
- column="60"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" FontResourcesParserCompat.FontFamilyFilesResourceEntry entry, Resources resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="201"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" FontResourcesParserCompat.FontFamilyFilesResourceEntry entry, Resources resources,"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="201"
- column="75"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Typeface createFromFontInfo(Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="226"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Context context, Resources resources, int id, String path, int style) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="290"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Context context, Resources resources, int id, String path, int style) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="290"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Context context, Resources resources, int id, String path, int style) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="290"
- column="59"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Class<?> obtainFontFamily() throws ClassNotFoundException {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="315"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Constructor<?> obtainFontFamilyCtor(Class<?> fontFamily)"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="319"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Constructor<?> obtainFontFamilyCtor(Class<?> fontFamily)"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="319"
- column="51"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainAddFontFromAssetManagerMethod(Class<?> fontFamily)"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="324"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainAddFontFromAssetManagerMethod(Class<?> fontFamily)"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="324"
- column="58"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainAddFontFromBufferMethod(Class<?> fontFamily)"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="331"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainAddFontFromBufferMethod(Class<?> fontFamily)"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="331"
- column="52"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainFreezeMethod(Class<?> fontFamily) throws NoSuchMethodException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="338"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainFreezeMethod(Class<?> fontFamily) throws NoSuchMethodException {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="338"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainAbortCreationMethod(Class<?> fontFamily) throws NoSuchMethodException {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="342"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainAbortCreationMethod(Class<?> fontFamily) throws NoSuchMethodException {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="342"
- column="48"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainCreateFromFamiliesWithDefaultMethod(Class<?> fontFamily)"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="346"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainCreateFromFamiliesWithDefaultMethod(Class<?> fontFamily)"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi26Impl.java"
- line="346"
- column="64"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Typeface createFromFamiliesWithDefault(Object family) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi28Impl.java"
- line="47"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Typeface createFromFamiliesWithDefault(Object family) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi28Impl.java"
- line="47"
- column="54"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainCreateFromFamiliesWithDefaultMethod(Class<?> fontFamily)"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi28Impl.java"
- line="59"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Method obtainCreateFromFamiliesWithDefaultMethod(Class<?> fontFamily)"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi28Impl.java"
- line="59"
- column="64"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected FontsContractCompat.FontInfo findBestInfo(FontsContractCompat.FontInfo[] fonts,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="46"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected FontsContractCompat.FontInfo findBestInfo(FontsContractCompat.FontInfo[] fonts,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="46"
- column="57"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Typeface createFromInputStream(Context context, InputStream is) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="53"
- column="15"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Typeface createFromInputStream(Context context, InputStream is) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="53"
- column="46"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" protected Typeface createFromInputStream(Context context, InputStream is) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="53"
- column="63"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Typeface createFromFontInfo(Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="59"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Typeface createFromFontFamilyFilesResourceEntry(Context context,"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="105"
- column="60"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" FontResourcesParserCompat.FontFamilyFilesResourceEntry familyEntry, Resources resources,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="106"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" FontResourcesParserCompat.FontFamilyFilesResourceEntry familyEntry, Resources resources,"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="106"
- column="81"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Context context, Resources resources, int id, String path, int style) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="151"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Context context, Resources resources, int id, String path, int style) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="151"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Context context, Resources resources, int id, String path, int style) {"
- errorLine2=" ~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatApi29Impl.java"
- line="151"
- column="59"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static File getTempFile(Context context) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="68"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ByteBuffer mmap(Context context, CancellationSignal cancellationSignal, Uri uri) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="108"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ByteBuffer mmap(Context context, CancellationSignal cancellationSignal, Uri uri) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="108"
- column="52"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ByteBuffer mmap(Context context, CancellationSignal cancellationSignal, Uri uri) {"
- errorLine2=" ~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="108"
- column="91"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ByteBuffer copyToDirectBuffer(Context context, Resources res, int id) {"
- errorLine2=" ~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="129"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static ByteBuffer copyToDirectBuffer(Context context, Resources res, int id) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="129"
- column="66"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean copyToFile(File file, InputStream is) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="147"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean copyToFile(File file, InputStream is) {"
- errorLine2=" ~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="147"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean copyToFile(File file, Resources res, int id) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="170"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean copyToFile(File file, Resources res, int id) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="170"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void closeQuietly(Closeable c) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/TypefaceCompatUtil.java"
- line="180"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getXVelocity(VelocityTracker tracker, int pointerId) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/VelocityTrackerCompat.java"
- line="36"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getYVelocity(VelocityTracker tracker, int pointerId) {"
- errorLine2=" ~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/VelocityTrackerCompat.java"
- line="48"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean canScrollHorizontally(View view, int direction) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="543"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean canScrollVertically(View view, int direction) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="556"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getOverScrollMode(View v) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="573"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setOverScrollMode(View v, @OverScroll int overScrollMode) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="593"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="633"
- column="53"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="633"
- column="61"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="665"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="665"
- column="63"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getAlpha(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1469"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setLayerType(View view, @LayerType int layerType, Paint paint) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1508"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setLayerType(View view, @LayerType int layerType, Paint paint) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1508"
- column="74"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getLayerType(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1532"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean isOpaque(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1700"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getMeasuredWidthAndState(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1737"
- column="48"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getMeasuredHeightAndState(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1754"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getMeasuredState(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1768"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getTranslationX(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1960"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getTranslationY(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1974"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static Matrix getMatrix(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="1996"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setTranslationX(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2102"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setTranslationY(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2119"
- column="40"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setAlpha(View view, @FloatRange(from = 0.0, to = 1.0) float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2136"
- column="33"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setX(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2151"
- column="29"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setY(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2166"
- column="29"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setRotation(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2179"
- column="36"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setRotationX(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2193"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setRotationY(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2207"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setScaleX(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2220"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setScaleY(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2233"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getPivotX(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2244"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setPivotX(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2260"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getPivotY(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2273"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setPivotY(View view, float value) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2289"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getRotation(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2297"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getRotationX(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2305"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getRotationY(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2313"
- column="38"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getScaleX(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2321"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getScaleY(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2329"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getX(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2337"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static float getY(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2345"
- column="30"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setChildrenDrawingOrderEnabled(ViewGroup viewGroup, boolean enabled) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2469"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setFitsSystemWindows(View view, boolean fitSystemWindows) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2510"
- column="45"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void jumpDrawablesToCurrentState(View v) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2524"
- column="52"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setSaveFromParentEnabled(View v, boolean enabled) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2930"
- column="49"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setActivated(View view, boolean activated) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewCompat.java"
- line="2946"
- column="37"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getScaledPagingTouchSlop(ViewConfiguration config) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewConfigurationCompat.java"
- line="56"
- column="48"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean hasPermanentMenuKey(ViewConfiguration config) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewConfigurationCompat.java"
- line="67"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static int getScaledHoverSlop(ViewConfiguration config) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewConfigurationCompat.java"
- line="127"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(ViewConfiguration config,"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewConfigurationCompat.java"
- line="139"
- column="70"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewGroupCompat.java"
- line="73"
- column="59"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onRequestSendAccessibilityEvent(ViewGroup group, View child,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewGroupCompat.java"
- line="73"
- column="76"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewGroupCompat.java"
- line="74"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void setMotionEventSplittingEnabled(ViewGroup group, boolean split) {"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewGroupCompat.java"
- line="97"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" ViewParent parent, View child, AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="65"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" ViewParent parent, View child, AccessibilityEvent event) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="65"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" ViewParent parent, View child, AccessibilityEvent event) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="65"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onStartNestedScroll(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="83"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onStartNestedScroll(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="83"
- column="66"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onStartNestedScroll(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="83"
- column="78"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScrollAccepted(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="100"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScrollAccepted(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="100"
- column="66"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScrollAccepted(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="100"
- column="78"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onStopNestedScroll(ViewParent parent, View target) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="113"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onStopNestedScroll(ViewParent parent, View target) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="113"
- column="62"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="130"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="130"
- column="58"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="158"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="158"
- column="58"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="176"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="176"
- column="61"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" int[] consumed) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="177"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onStartNestedScroll(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="205"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onStartNestedScroll(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="205"
- column="66"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onStartNestedScroll(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="205"
- column="78"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScrollAccepted(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="246"
- column="47"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScrollAccepted(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="246"
- column="66"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScrollAccepted(ViewParent parent, View child, View target,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="246"
- column="78"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onStopNestedScroll(ViewParent parent, View target, int type) {"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="281"
- column="43"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onStopNestedScroll(ViewParent parent, View target, int type) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="281"
- column="62"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="325"
- column="39"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="325"
- column="58"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="382"
- column="42"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="382"
- column="61"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" int[] consumed, int type) {"
- errorLine2=" ~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="383"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onNestedFling(ViewParent parent, View target, float velocityX,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="422"
- column="41"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onNestedFling(ViewParent parent, View target, float velocityX,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="422"
- column="60"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onNestedPreFling(ViewParent parent, View target, float velocityX,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="459"
- column="44"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static boolean onNestedPreFling(ViewParent parent, View target, float velocityX,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="459"
- column="63"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,"
- errorLine2=" ~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="490"
- column="63"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public static void notifySubtreeAccessibilityStateChanged(ViewParent parent, View child,"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="490"
- column="82"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" View source, int changeType) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewParentCompat.java"
- line="491"
- column="13"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat setDuration(long value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="121"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat alpha(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="136"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat alphaBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="151"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat translationX(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="166"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat translationY(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="181"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat withEndAction(Runnable runnable) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="214"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat withEndAction(Runnable runnable) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="214"
- column="53"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat setInterpolator(Interpolator value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="252"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat setInterpolator(Interpolator value) {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="252"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public Interpolator getInterpolator() {"
- errorLine2=" ~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="265"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat setStartDelay(long value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="284"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat rotation(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="316"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat rotationBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="331"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat rotationX(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="346"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat rotationXBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="361"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat rotationY(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="376"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat rotationYBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="391"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat scaleX(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="406"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat scaleXBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="421"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat scaleY(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="436"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat scaleYBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="451"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat x(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="476"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat xBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="491"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat y(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="506"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat yBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="521"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat translationXBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="536"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat translationYBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="551"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat translationZBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="568"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat translationZ(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="587"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat z(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="606"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat zBy(float value) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="625"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat withLayer() {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="678"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat withStartAction(Runnable runnable) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="706"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat withStartAction(Runnable runnable) {"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="706"
- column="55"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat setListener(final ViewPropertyAnimatorListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="727"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat setListener(final ViewPropertyAnimatorListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="727"
- column="57"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public ViewPropertyAnimatorCompat setUpdateListener("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="773"
- column="12"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" final ViewPropertyAnimatorUpdateListener listener) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorCompat.java"
- line="774"
- column="19"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" void onAnimationStart(View view);"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorListener.java"
- line="32"
- column="27"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" void onAnimationEnd(View view);"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorListener.java"
- line="40"
- column="25"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" void onAnimationCancel(View view);"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorListener.java"
- line="48"
- column="28"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onAnimationStart(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorListenerAdapter.java"
- line="33"
- column="34"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onAnimationEnd(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorListenerAdapter.java"
- line="40"
- column="32"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" public void onAnimationCancel(View view) {"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorListenerAdapter.java"
- line="47"
- column="35"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" void onAnimationUpdate(View view);"
- errorLine2=" ~~~~">
- <location
- file="src/main/java/androidx/core/view/ViewPropertyAnimatorUpdateListener.java"
- line="34"
- column="28"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" Drawable getWrappedDrawable();"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/WrappedDrawable.java"
- line="33"
- column="5"/>
- </issue>
-
- <issue
- id="UnknownNullness"
- message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
- errorLine1=" void setWrappedDrawable(Drawable drawable);"
- errorLine2=" ~~~~~~~~">
- <location
- file="src/main/java/androidx/core/graphics/drawable/WrappedDrawable.java"
- line="34"
- column="29"/>
- </issue>
-
-</issues>
diff --git a/appsearch/appsearch-builtin-types/OWNERS b/appsearch/appsearch-builtin-types/OWNERS
new file mode 100644
index 0000000..e863f7d
--- /dev/null
+++ b/appsearch/appsearch-builtin-types/OWNERS
@@ -0,0 +1,4 @@
+ddong@google.com
+saurabhkb@google.com
+quazi@google.com
+pinyaoting@google.com
diff --git a/benchmark/benchmark-common/build.gradle b/benchmark/benchmark-common/build.gradle
index 47f2c19..cceacc9 100644
--- a/benchmark/benchmark-common/build.gradle
+++ b/benchmark/benchmark-common/build.gradle
@@ -78,3 +78,15 @@
freeCompilerArgs += ["-Xopt-in=kotlin.Experimental"]
}
}
+
+// https://github.com/square/wire/issues/1947
+// Remove when we upgrade to fixed wire library
+afterEvaluate {
+ tasks.named("compileReleaseKotlin").configure {
+ it.dependsOn("generateDebugProtos")
+ }
+ tasks.named("compileDebugKotlin").configure {
+ it.dependsOn("generateReleaseProtos")
+ }
+}
+
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
index 6a11376..19cc93a 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
@@ -185,12 +185,9 @@
)
}
}
- // Kill Process
+ // Kill process before compiling
Log.d(TAG, "Killing process $packageName")
Shell.executeCommand("am force-stop $packageName")
- // Compile
- compilePackage(packageName)
- Log.d(TAG, "$packageName is compiled.")
} else if (this is SpeedProfile) {
repeat(this.warmupIterations) {
block()
@@ -204,8 +201,8 @@
Log.d(TAG, "Received dump profile response $response")
throw RuntimeException("Failed to dump profile for $packageName ($response)")
}
- compilePackage(packageName)
}
+ compilePackage(packageName)
}
/**
diff --git a/benchmark/integration-tests/macrobenchmark-target/build.gradle b/benchmark/integration-tests/macrobenchmark-target/build.gradle
index 2ef7963..20dd547 100644
--- a/benchmark/integration-tests/macrobenchmark-target/build.gradle
+++ b/benchmark/integration-tests/macrobenchmark-target/build.gradle
@@ -35,6 +35,7 @@
implementation(libs.constraintLayout)
implementation(project(":appcompat:appcompat"))
implementation(project(":activity:activity"))
+ implementation(project(":profileinstaller:profileinstaller"))
implementation(project(":recyclerview:recyclerview"))
implementation(project(":tracing:tracing-ktx"))
implementation(libs.material)
diff --git a/biometric/biometric/build.gradle b/biometric/biometric/build.gradle
index 3ac9baf..b655629 100644
--- a/biometric/biometric/build.gradle
+++ b/biometric/biometric/build.gradle
@@ -62,11 +62,6 @@
testOptions.unitTests.includeAndroidResources = true
}
-// Robolectric requires more memory than Gradle default
-tasks.withType(Test) { t ->
- t.maxHeapSize = "1024m"
-}
-
androidx {
name = "Biometric"
publish = Publish.SNAPSHOT_AND_RELEASE
diff --git a/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt b/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt
index 0f02efd..6530957 100644
--- a/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt
+++ b/buildSrc-tests/project-subsets/src/test/kotlin/androidx/build/ProjectSubsetsTest.kt
@@ -63,7 +63,9 @@
}
GradleRunner.create()
.withProjectDir(projectDir)
- .withArguments("-Pandroidx.projects=$name", "tasks")
+ // TODO(b/207407626) remove --no-configuration-cache once changing the subset works
+ // correctly
+ .withArguments("-Pandroidx.projects=$name", "tasks", "--no-configuration-cache")
.withTestKitDir(File(outDir, ".gradle-testkit"))
.build(); // fails the test if the build fails
}
diff --git a/buildSrc/lint.xml b/buildSrc/lint.xml
index 12cad2a..5ec6632 100644
--- a/buildSrc/lint.xml
+++ b/buildSrc/lint.xml
@@ -1,5 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Please copy any lint rules added here to all other modules local lint.xml files -->
+<!--
+ 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.
+ -->
+
+<!--
+ Please copy any lint rules added here to all other modules local lint.xml
+ files and lint_samples.xml (if applicable).
+ -->
<lint>
<issue id="LintBaseline" severity="ignore">
<ignore regexp=".* filtered out because .* listed in the baseline file.*"/>
diff --git a/buildSrc/lint_samples.xml b/buildSrc/lint_samples.xml
new file mode 100644
index 0000000..8ec48c4
--- /dev/null
+++ b/buildSrc/lint_samples.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!--
+ This configuration is applied to LibraryType.SAMPLES projects which must run
+ specific lint checks on dependencies, but don't want the full suite running.
+ -->
+<lint>
+ <issue id="LintBaseline" severity="ignore">
+ <ignore regexp=".* filtered out because .* listed in the baseline file.*"/>
+ </issue>
+ <!-- Disable all lint checks in non-sample projects. -->
+ <issue id="all">
+ <ignore regexp="^((?!samples).)*$" />
+ </issue>
+ <!-- Re-enable sampled lint checks for all projects. -->
+ <issue id="InvalidSamplesLocation" severity="fatal" />
+ <issue id="MultipleSampledFunctions" severity="fatal" />
+ <issue id="UnresolvedSampleLink" severity="fatal" />
+ <issue id="ObsoleteSampledAnnotation" severity="fatal" />
+</lint>
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
index a06cdbd..b003840 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
@@ -146,6 +146,7 @@
error.add("ModifierFactoryReturnType")
error.add("ModifierFactoryExtensionFunction")
error.add("ModifierParameter")
+ error.add("MutableCollectionMutableState")
error.add("UnnecessaryComposedModifier")
// Paths we want to enable ListIterator checks for - for higher level
@@ -178,16 +179,13 @@
}
}
- // TODO(148540713): remove this exclusion when Lint can support using multiple lint jars
- configurations.getByName("lintChecks").exclude(
- mapOf("module" to "lint-checks")
- )
// TODO: figure out how to apply this to multiplatform modules
dependencies.add(
"lintChecks",
project.dependencies.project(
mapOf(
"path" to ":compose:lint:internal-lint-checks",
+ // TODO(b/206617878) remove this shadow configuration
"configuration" to "shadow"
)
)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index e377245..f359495 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -113,6 +113,7 @@
project.configureTaskTimeouts()
project.configureMavenArtifactUpload(extension)
+ project.configureExportLibraryGroupsToXml()
project.configureExternalDependencyLicenseCheck()
project.configureProjectStructureValidation(extension)
project.configureProjectVersionValidation(extension)
@@ -136,6 +137,9 @@
private fun configureTestTask(project: Project, task: Test) {
AffectedModuleDetector.configureTaskGuard(task)
+ // Robolectric 1.7 increased heap size requirements, see b/207169653.
+ task.maxHeapSize = "2g"
+
val xmlReportDestDir = project.getHostTestResultDirectory()
val archiveName = "${project.path.asFilenamePrefix()}_${task.name}.zip"
if (project.isDisplayTestOutput()) {
@@ -394,6 +398,17 @@
project.addToProjectMap(extension)
}
+ private fun Project.configureExportLibraryGroupsToXml() {
+ project.tasks.register(
+ "exportLibraryGroupsToXml",
+ ExportLibraryGroupsToXmlTask::class.java
+ ) { task ->
+ task.libraryGroupFile = project.file("${project.getSupportRootFolder()}" +
+ "/buildSrc/public/src/main/kotlin/androidx/build/LibraryGroups.kt")
+ task.xmlOutputFile = project.file("${project.buildDir}/lint/library-groups.xml")
+ }
+ }
+
private fun Project.configureProjectStructureValidation(
extension: AndroidXExtension
) {
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/ExportLibraryGroupsToXmlTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/ExportLibraryGroupsToXmlTask.kt
new file mode 100644
index 0000000..bb8df4a
--- /dev/null
+++ b/buildSrc/private/src/main/kotlin/androidx/build/ExportLibraryGroupsToXmlTask.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.build
+
+import com.google.common.io.Files
+import org.gradle.api.DefaultTask
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import java.io.BufferedWriter
+import java.io.File
+import java.io.Writer
+import kotlin.reflect.full.memberProperties
+import kotlin.text.Charsets.UTF_8
+
+/**
+ * Task that parses the contents of a given library group file (usually [LibraryGroups]) and writes
+ * them to an XML file. The XML file is then used by Lint.
+ */
+@CacheableTask
+abstract class ExportLibraryGroupsToXmlTask : DefaultTask() {
+
+ @get:[InputFile PathSensitive(PathSensitivity.NONE)]
+ lateinit var libraryGroupFile: File
+
+ @get:OutputFile
+ lateinit var xmlOutputFile: File
+
+ @TaskAction
+ fun exec() {
+ val writer: Writer = BufferedWriter(Files.newWriter(xmlOutputFile, UTF_8))
+
+ // Write XML header and outermost opening tag
+ writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ writer.write("<libraryGroups>\n")
+
+ LibraryGroups::class.memberProperties.forEach { member ->
+ try {
+ val libraryGroup = member.get(LibraryGroups) as LibraryGroup
+ val groupName = libraryGroup.group
+ val isAtomic = (libraryGroup.forcedVersion != null)
+
+ // Write data for this LibraryGroup
+ writer.run {
+ write(" <libraryGroup>\n")
+ write(" <group>$groupName</group>\n")
+ write(" <isAtomic>$isAtomic</isAtomic>\n")
+ write(" </libraryGroup>\n")
+ }
+ } catch (ignore: ClassCastException) {
+ // Object isn't a LibraryGroup, skip it
+ }
+ }
+
+ // Write outermost closing tag and close writer
+ writer.write("<libraryGroups>\n")
+ writer.close()
+ }
+}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
index 97cc2cb..05effe5 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
@@ -88,7 +88,6 @@
it.enabled = false
}
tasks.register("lintAnalyzeDebug") {
- it.dependsOn(lintTask)
it.enabled = false
}
tasks.register("lintRelease") {
@@ -108,7 +107,6 @@
setUpLintDebugIfNeeded()
}
tasks.register("lintAnalyze") {
- it.dependsOn("lintDebug")
it.enabled = false
}
configureLint(lint, extension)
@@ -303,13 +301,6 @@
// This is needed because SampledAnnotationDetector uses partial analysis, and
// hence requires dependencies to be analyzed.
checkDependencies = true
- // TODO: baselines from dependencies aren't used when we run lint with
- // isCheckDependencies = true. NewApi was recently enabled for tests, and so
- // there are a large amount of baselined issues that would be reported here
- // again, and we don't want to add them to the baseline for the sample modules.
- // Instead just temporarily disable this lint check until the underlying issues
- // are fixed.
- disable.add("NewApi")
}
// Only run certain checks where API tracking is important.
@@ -319,9 +310,14 @@
// If the project has not overridden the lint config, set the default one.
if (lintConfig == null) {
+ val lintXmlPath = if (extension.type == LibraryType.SAMPLES) {
+ "buildSrc/lint_samples.xml"
+ } else {
+ "buildSrc/lint.xml"
+ }
// suppress warnings more specifically than issue-wide severity (regexes)
// Currently suppresses warnings from baseline files working as intended
- lintConfig = File(project.getSupportRootFolder(), "buildSrc/lint.xml")
+ lintConfig = File(project.getSupportRootFolder(), lintXmlPath)
}
// Ideally, teams aren't able to add new violations to a baseline file; they should only
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 cb6f0f2..47ac289 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
@@ -169,10 +169,13 @@
Sync::class.java
) { task ->
val sources = docsConfiguration.incoming.artifactView { }.files
+ // Store archiveOperations into a local variable to prevent access to the plugin
+ // during the task execution, as that breaks configuration caching.
+ val localVar = archiveOperations
task.from(
sources.elements.map { jars ->
jars.map {
- archiveOperations.zipTree(it).matching {
+ localVar.zipTree(it).matching {
// Filter out files that documentation tools cannot process.
it.exclude("**/*.MF")
it.exclude("**/*.aidl")
@@ -206,13 +209,15 @@
docsConfiguration: Configuration
): TaskProvider<Sync> {
return project.tasks.register("unzipSourcesForDackka", Sync::class.java) { task ->
-
val sources = docsConfiguration.incoming.artifactView { }.files
+ // Store archiveOperations into a local variable to prevent access to the plugin
+ // during the task execution, as that breaks configuration caching.
+ val localVar = archiveOperations
task.from(
sources.elements.map { jars ->
jars.map {
- archiveOperations.zipTree(it)
+ localVar.zipTree(it)
}
}
)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt b/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
index 523e202..83676f6 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/uptodatedness/TaskUpToDateValidator.kt
@@ -181,19 +181,6 @@
"validateProperties",
"tasks",
- ":benchmark:benchmark-common:generateReleaseProtos",
- ":benchmark:benchmark-common:generateDebugProtos",
- ":benchmark:benchmark-common:compileReleaseKotlin",
- ":benchmark:benchmark-common:compileDebugKotlin",
- ":benchmark:benchmark-common:compileReleaseJavaWithJavac",
- ":benchmark:benchmark-common:compileDebugJavaWithJavac",
- ":benchmark:benchmark-common:extractReleaseAnnotations",
- ":benchmark:benchmark-common:extractDebugAnnotations",
- ":benchmark:benchmark-common:generateApi",
- ":benchmark:benchmark-common:runErrorProne",
- ":benchmark:benchmark-common:lintAnalyzeDebug",
- ":benchmark:benchmark-common:lintDebug",
-
// More information about the fact that these dokka tasks rerun can be found at b/167569304
"dokkaKotlinDocs",
"zipDokkaDocs",
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/public/src/main/kotlin/androidx/build/LibraryVersions.kt
index 9522e20..557195d 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -23,7 +23,7 @@
val ACTIVITY = Version("1.5.0-alpha01")
val ADS_IDENTIFIER = Version("1.0.0-alpha05")
val ANNOTATION = Version("1.4.0-alpha01")
- val ANNOTATION_EXPERIMENTAL = Version("1.2.0-beta02")
+ val ANNOTATION_EXPERIMENTAL = Version("1.2.0-rc01")
val APPCOMPAT = Version("1.5.0-alpha01")
val APPSEARCH = Version("1.0.0-alpha05")
val ARCH_CORE = Version("2.2.0-alpha01")
@@ -44,12 +44,13 @@
val CONTENTPAGER = Version("1.1.0-alpha01")
val COMPOSE_MATERIAL3 = Version(System.getenv("COMPOSE_CUSTOM_VERSION") ?: "1.0.0-alpha02")
val COMPOSE = Version(System.getenv("COMPOSE_CUSTOM_VERSION") ?: "1.1.0-beta04")
- val COORDINATORLAYOUT = Version("1.2.0-beta02")
+ val COORDINATORLAYOUT = Version("1.2.0-rc01")
val CORE = Version("1.8.0-alpha01")
val CORE_ANIMATION = Version("1.0.0-alpha03")
val CORE_ANIMATION_TESTING = Version("1.0.0-alpha03")
val CORE_APPDIGEST = Version("1.0.0-alpha01")
val CORE_GOOGLE_SHORTCUTS = Version("1.1.0-alpha02")
+ val CORE_PERFORMANCE = Version("1.0.0-alpha01")
val CORE_REMOTEVIEWS = Version("1.0.0-alpha01")
val CORE_ROLE = Version("1.1.0-alpha02")
val CORE_SPLASHSCREEN = Version("1.0.0-alpha03")
@@ -104,7 +105,7 @@
val RECYCLERVIEW_SELECTION = Version("1.2.0-alpha02")
val REMOTECALLBACK = Version("1.0.0-alpha02")
val RESOURCEINSPECTION = Version("1.1.0-alpha01")
- val ROOM = Version("2.4.0-beta03")
+ val ROOM = Version("2.5.0-alpha01")
val SAVEDSTATE = Version("1.2.0-alpha01")
val SECURITY = Version("1.1.0-alpha04")
val SECURITY_APP_AUTHENTICATOR = Version("1.0.0-alpha03")
@@ -118,7 +119,7 @@
val SLICE_REMOTECALLBACK = Version("1.0.0-alpha01")
val SLIDINGPANELAYOUT = Version("1.2.0-rc01")
val STARTUP = Version("1.2.0-alpha01")
- val SQLITE = Version("2.2.0-beta03")
+ val SQLITE = Version("2.3.0-alpha01")
val SQLITE_INSPECTOR = Version("2.1.0-alpha01")
val SWIPEREFRESHLAYOUT = Version("1.2.0-alpha01")
val TESTSCREENSHOT = Version("1.0.0-alpha01")
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt b/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt
index 8161bc3..8ba49ed 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt
@@ -91,7 +91,31 @@
* @return the root project's platform-specific NDK path as a file.
*/
fun Project.getNdkPath(): File {
- return File(getSdkPath(), "ndk")
+ val sdkPath = getSdkPath()
+ return getPathFromEnvironmentVariableOrNull("ANDROID_NDK_ROOT")
+ // One of the default paths when ndk is installed for the first time via sdkmanager.
+ ?: fileIfExistsOrNull(sdkPath, "ndk-bundle")
+ ?: File(sdkPath, "ndk")
+}
+
+/**
+ * @return [File] representing the path stored in [envValue] if it exists, `null` otherwise.
+ */
+private fun getPathFromEnvironmentVariableOrNull(envVar: String): File? {
+ val envValue = System.getenv(envVar)
+ if (envValue != null) {
+ val dir = File(envValue)
+ if (dir.isDirectory) {
+ return dir
+ }
+ }
+
+ return null
+}
+
+private fun fileIfExistsOrNull(parent: File, child: String): File? {
+ val file = File(parent, child)
+ return if (file.exists()) file else null
}
private fun getSdkPathFromEnvironmentVariable(): File {
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt b/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
index 931fa0a..48197b5 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/SupportConfig.kt
@@ -27,13 +27,7 @@
const val DEFAULT_MIN_SDK_VERSION = 14
const val INSTRUMENTATION_RUNNER = "androidx.test.runner.AndroidJUnitRunner"
const val BUILD_TOOLS_VERSION = "30.0.3"
- val NDK_VERSION by lazy {
- // TODO(aurimas) b/173737578 remove when we no longer have divergent versions
- when (getOperatingSystem()) {
- OperatingSystem.LINUX -> "23.0.7243079"
- else -> "23.0.7599858"
- }
- }
+ const val NDK_VERSION = "23.1.7779620"
/**
* The Android SDK version to use for compilation.
diff --git a/busytown/androidx-studio-integration-lint.sh b/busytown/androidx-studio-integration-lint.sh
index a1d24aa..1ebe930 100755
--- a/busytown/androidx-studio-integration-lint.sh
+++ b/busytown/androidx-studio-integration-lint.sh
@@ -6,8 +6,7 @@
-Pandroidx.summarizeStderr \
-Pandroidx.verifyUpToDate \
-x verifyDependencyVersions \
- lint \
- lintDebug \
- lintReport \
+ lintAnalyze \
+ lintAnalyzeDebug \
--no-daemon \
--stacktrace
diff --git a/busytown/androidx.sh b/busytown/androidx.sh
index dcdefc3..5c5f28b 100755
--- a/busytown/androidx.sh
+++ b/busytown/androidx.sh
@@ -5,19 +5,24 @@
cd "$(dirname $0)"
-# Run Gradle
EXIT_VALUE=0
-if impl/build.sh buildOnServer checkExternalLicenses listTaskOutputs validateAllProperties \
- -Pandroidx.enableComposeCompilerMetrics=true \
- -Pandroidx.enableComposeCompilerReports=true \
- --profile "$@"; then
- EXIT_VALUE=0
-else
- EXIT_VALUE=1
-fi
-# Parse performance profile reports (generated with the --profile option above) and re-export the metrics in an easily machine-readable format for tracking
-impl/parse_profile_htmls.sh
+# Validate translation exports, if present
+if ! impl/check_translations.sh; then
+ EXIT_VALUE=1
+else
+ # Run Gradle
+ if ! impl/build.sh buildOnServer checkExternalLicenses listTaskOutputs validateAllProperties \
+ -Pandroidx.enableComposeCompilerMetrics=true \
+ -Pandroidx.enableComposeCompilerReports=true \
+ --profile "$@"; then
+ EXIT_VALUE=1
+ fi
+
+ # Parse performance profile reports (generated with the --profile option above) and re-export
+ # the metrics in an easily machine-readable format for tracking
+ impl/parse_profile_htmls.sh
+fi
echo "Completing $0 at $(date) with exit value $EXIT_VALUE"
diff --git a/busytown/impl/build.sh b/busytown/impl/build.sh
index 2efd41f..5982749 100755
--- a/busytown/impl/build.sh
+++ b/busytown/impl/build.sh
@@ -19,6 +19,10 @@
mkdir -p "$DIST_DIR"
export DIST_DIR="$DIST_DIR"
+if [ "$CHANGE_INFO" != "" ]; then
+ cp "$CHANGE_INFO" "$DIST_DIR/"
+fi
+
# parse arguments
if [ "$1" == "--diagnose" ]; then
DIAGNOSE=true
@@ -60,10 +64,12 @@
# export some variables
ANDROID_HOME=../../prebuilts/fullsdk-linux
+EXIT_VALUE=0
# run the build
if run ./gradlew --ci saveSystemStats "$@"; then
echo build passed
else
+ EXIT_VALUE=1
if [ "$DIAGNOSE" == "true" ]; then
# see if diagnose-build-failure.sh can identify the root cauase
echo "running diagnose-build-failure.sh, see build.log" >&2
@@ -73,7 +79,13 @@
# more easily investigate further
./development/diagnose-build-failure/diagnose-build-failure.sh --timeout 600 "--ci saveSystemStats $*"
fi
- exit 1
+fi
+
+#export build scan information
+zip -q -r $DIST_DIR/build_scans.zip $OUT_DIR/.gradle/build-scan-data || true
+
+if [ "$EXIT_VALUE" != "0" ]; then
+ exit "$EXIT_VALUE"
fi
# check that no unexpected modifications were made to the source repository, such as new cache directories
diff --git a/busytown/impl/check_translations.sh b/busytown/impl/check_translations.sh
new file mode 100755
index 0000000..f18ec2b
--- /dev/null
+++ b/busytown/impl/check_translations.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+set -e
+
+# This script verifies the set of translatable string.xml fields against
+# those listed in the localization exports file.
+
+cd "$(dirname "$0")/../.."
+
+exports='../../vendor/google/tools/localization/exports'
+if [ ! -e "$exports" ]; then
+ echo 'Missing localization exports file, skipping verification...'
+ exit 0
+fi
+
+tempdir=$(mktemp -d "${OUT_DIR:-/tmp/}$(basename "$0").XXXXXXXXXXXX")
+trap 'rm -rf "$tempdir"' EXIT
+
+# Find string.xml files that need translation
+expect="$tempdir/expect.txt"
+find . \
+ \( \
+ -iname '*samples*' \
+ -o -iname '*debug*' \
+ -o -iname '*test*' \
+ \) \
+ -prune -o \
+ -path '*/res/values/strings.xml' \
+ -print \
+ | sed -n 's/.\///p' \
+ | sort \
+ > "$expect"
+
+# Scrape string.xml files for platform branch
+actual="$tempdir/actual.txt"
+grep 'androidx-platform-dev' "$exports" \
+ | grep -Eo '[^ ]+/strings\.xml' \
+ | sort \
+ > "$actual"
+
+# Compare and report
+diff=$(diff "${expect}" "${actual}" | { grep '<' || true; })
+if [ -n "$diff" ]; then
+ echo "Missing files in $exports:" &> 2
+ diff "$expect" "$actual" | grep strings.xml | sed -n 's/< //p' &> 2
+ echo &> 2
+ echo 'See go/androidx/playbook#translations for more information' &> 2
+ exit 1
+fi
diff --git a/camera/build.gradle b/camera/build.gradle
deleted file mode 100644
index 7797bec..0000000
--- a/camera/build.gradle
+++ /dev/null
@@ -1,6 +0,0 @@
-// b/155305020 , b/157663450 : temporarily increase test memory to see if it affects crash rate
-allprojects { pr ->
- pr.tasks.withType(Test) { t ->
- t.maxHeapSize = "2048m"
- }
-}
diff --git a/camera/camera-camera2-pipe/build.gradle b/camera/camera-camera2-pipe/build.gradle
index af4e291..3cdad88 100644
--- a/camera/camera-camera2-pipe/build.gradle
+++ b/camera/camera-camera2-pipe/build.gradle
@@ -43,7 +43,7 @@
testImplementation(libs.testRunner)
testImplementation(libs.junit)
testImplementation(libs.truth)
- testImplementation(libs.robolectric)
+ testImplementation("org.robolectric:robolectric:4.6.1") // TODO(b/205731854): fix tests to work with SDK 31 and robolectric 4.7
testImplementation(libs.kotlinCoroutinesTest)
testImplementation(project(":camera:camera-camera2-pipe-testing"))
testImplementation(project(":internal-testutils-truth"))
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 11c1c32..386a9c0 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -38,7 +38,7 @@
testImplementation(libs.testRunner)
testImplementation(libs.junit)
testImplementation(libs.truth)
- testImplementation(libs.robolectric)
+ testImplementation("org.robolectric:robolectric:4.6.1") // TODO(b/205731854): fix tests to work with SDK 31 and robolectric 4.7
testImplementation(libs.mockitoCore)
testImplementation(libs.kotlinCoroutinesTest)
testImplementation("androidx.annotation:annotation-experimental:1.1.0")
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt
index a48140c..59cd140 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/compat/workaround/ExtraSupportedSurfaceCombinationsContainerDeviceTest.kt
@@ -20,10 +20,12 @@
import android.graphics.ImageFormat
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraManager
+import android.os.Handler
+import android.os.Looper
import android.util.Size
import android.view.Surface
import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.internal.Camera2CameraFactory
import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks
import androidx.camera.camera2.internal.compat.quirk.ExtraSupportedSurfaceCombinationsQuirk
import androidx.camera.camera2.interop.Camera2CameraInfo
@@ -35,6 +37,7 @@
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.core.impl.CameraInfoInternal
+import androidx.camera.core.impl.CameraThreadConfig
import androidx.camera.core.impl.CaptureProcessor
import androidx.camera.core.impl.ImageProxyBundle
import androidx.camera.core.impl.SurfaceCombination
@@ -79,13 +82,17 @@
companion object {
@JvmStatic
@Parameterized.Parameters(name = "cameraId = {0}")
- fun initParameters(): List<String> = getCameraIds()
+ fun initParameters(): MutableSet<String> = getCameraIds()
- private fun getCameraIds(): List<String> {
- val cameraManager = ApplicationProvider.getApplicationContext<Context>()
- .getSystemService(Context.CAMERA_SERVICE) as CameraManager
-
- return cameraManager.cameraIdList.asList()
+ private fun getCameraIds(): MutableSet<String> {
+ val camera2CameraFactory = Camera2CameraFactory(
+ ApplicationProvider.getApplicationContext(),
+ CameraThreadConfig.create(
+ CameraXExecutors.mainThreadExecutor(),
+ Handler(Looper.getMainLooper())
+ ),
+ null)
+ return camera2CameraFactory.availableCameraIds
}
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraFactory.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraFactory.java
index 7d2ac60..9105f6f 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraFactory.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraFactory.java
@@ -16,7 +16,11 @@
package androidx.camera.camera2.internal;
+import static android.hardware.camera2.CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
+
import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -26,11 +30,13 @@
import androidx.camera.core.CameraSelector;
import androidx.camera.core.CameraUnavailableException;
import androidx.camera.core.InitializationException;
+import androidx.camera.core.Logger;
import androidx.camera.core.impl.CameraFactory;
import androidx.camera.core.impl.CameraInternal;
import androidx.camera.core.impl.CameraStateRegistry;
import androidx.camera.core.impl.CameraThreadConfig;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -42,6 +48,7 @@
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public final class Camera2CameraFactory implements CameraFactory {
+ private static final String TAG = "Camera2CameraFactory";
private static final int DEFAULT_ALLOWED_CONCURRENT_OPEN_CAMERAS = 1;
private final CameraThreadConfig mThreadConfig;
private final CameraStateRegistry mCameraStateRegistry;
@@ -57,8 +64,9 @@
mCameraStateRegistry = new CameraStateRegistry(DEFAULT_ALLOWED_CONCURRENT_OPEN_CAMERAS);
mCameraManager = CameraManagerCompat.from(context, mThreadConfig.getSchedulerHandler());
- mAvailableCameraIds = CameraSelectionOptimizer
- .getSelectedAvailableCameraIds(this, availableCamerasSelector);
+ List<String> optimizedCameraIds = CameraSelectionOptimizer.getSelectedAvailableCameraIds(
+ this, availableCamerasSelector);
+ mAvailableCameraIds = getBackwardCompatibleCameraIds(optimizedCameraIds);
}
@Override
@@ -102,4 +110,53 @@
public CameraManagerCompat getCameraManager() {
return mCameraManager;
}
+
+ private List<String> getBackwardCompatibleCameraIds(
+ @NonNull List<String> availableCameraIds) throws InitializationException {
+ List<String> backwardCompatibleCameraIds = new ArrayList<>();
+
+ for (String cameraId : availableCameraIds) {
+ // Skips camera id 0 and 1 because mostly they should be the ids of the back and
+ // front camera by default.
+ if (cameraId.equals("0") || cameraId.equals("1")) {
+ backwardCompatibleCameraIds.add(cameraId);
+ continue;
+ } else if (isBackwardCompatible(cameraId)) {
+ backwardCompatibleCameraIds.add(cameraId);
+ } else {
+ Logger.d(TAG, "Camera " + cameraId + " is filtered out because its capabilities "
+ + "do not contain REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE.");
+ }
+ }
+
+ return backwardCompatibleCameraIds;
+ }
+
+ private boolean isBackwardCompatible(@NonNull String cameraId) throws InitializationException {
+ // Always returns true to not break robolectric tests because the cameras setup in
+ // robolectric don't have REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE capability
+ // by default.
+ if ("robolectric".equals(Build.FINGERPRINT)) {
+ return true;
+ }
+
+ int[] availableCapabilities;
+
+ try {
+ availableCapabilities = mCameraManager.getCameraCharacteristicsCompat(cameraId).get(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ } catch (CameraAccessExceptionCompat e) {
+ throw new InitializationException(CameraUnavailableExceptionHelper.createFrom(e));
+ }
+
+ if (availableCapabilities != null) {
+ for (int capability : availableCapabilities) {
+ if (capability == REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
index 405073f..490a9a6 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManager.java
@@ -32,7 +32,6 @@
import androidx.camera.core.impl.UseCaseConfig;
import androidx.core.util.Preconditions;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -186,17 +185,6 @@
@NonNull List<UseCaseConfig<?>> newUseCaseConfigs) {
Preconditions.checkArgument(!newUseCaseConfigs.isEmpty(), "No new use cases to be bound.");
- // Use the small size (640x480) for new use cases to check whether there is any possible
- // supported combination first
- List<SurfaceConfig> surfaceConfigs = new ArrayList<>(existingSurfaces);
-
- for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
- surfaceConfigs.add(
- transformSurfaceConfig(cameraId,
- useCaseConfig.getInputFormat(),
- new Size(640, 480)));
- }
-
SupportedSurfaceCombination supportedSurfaceCombination =
mCameraSupportedSurfaceCombinationMap.get(cameraId);
@@ -205,14 +193,6 @@
+ cameraId);
}
- if (!supportedSurfaceCombination.checkSupported(surfaceConfigs)) {
- throw new IllegalArgumentException(
- "No supported surface combination is found for camera device - Id : "
- + cameraId + ". May be attempting to bind too many use cases. "
- + "Existing surfaces: " + existingSurfaces + " New configs: "
- + newUseCaseConfigs);
- }
-
return supportedSurfaceCombination.getSuggestedResolutions(existingSurfaces,
newUseCaseConfigs);
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
index 3ea7ebc..67803f3 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
@@ -25,7 +25,6 @@
import android.content.Context;
import android.hardware.camera2.CameraDevice;
-import android.hardware.display.DisplayManager;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@@ -43,10 +42,10 @@
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public final class Camera2UseCaseConfigFactory implements UseCaseConfigFactory {
- final DisplayManager mDisplayManager;
+ final DisplayInfoManager mDisplayInfoManager;
public Camera2UseCaseConfigFactory(@NonNull Context context) {
- mDisplayManager = DisplayUtil.getDisplayManager(context);
+ mDisplayInfoManager = DisplayInfoManager.getInstance(context);
}
/**
@@ -104,10 +103,10 @@
if (captureType == CaptureType.PREVIEW) {
mutableConfig.insertOption(OPTION_MAX_RESOLUTION,
- SupportedSurfaceCombination.getPreviewSize(mDisplayManager));
+ mDisplayInfoManager.getPreviewSize());
}
- int targetRotation = DisplayUtil.getMaxSizeDisplay(mDisplayManager).getRotation();
+ int targetRotation = mDisplayInfoManager.getMaxSizeDisplay().getRotation();
mutableConfig.insertOption(OPTION_TARGET_ROTATION, targetRotation);
return OptionsBundle.from(mutableConfig);
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/DisplayInfoManager.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/DisplayInfoManager.java
new file mode 100644
index 0000000..75a6bb8
--- /dev/null
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/DisplayInfoManager.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.internal;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.util.Size;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * A singleton class to retrieve display related information.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+class DisplayInfoManager {
+ private static final Size MAX_PREVIEW_SIZE = new Size(1920, 1080);
+ private static final Object INSTANCE_LOCK = new Object();
+ private static volatile DisplayInfoManager sInstance;
+ @NonNull
+ private final DisplayManager mDisplayManager;
+ private volatile Size mPreviewSize = null;
+ private DisplayInfoManager(@NonNull Context context) {
+ mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ }
+
+ static DisplayInfoManager getInstance(Context context) {
+ if (sInstance == null) {
+ synchronized (INSTANCE_LOCK) {
+ if (sInstance == null) {
+ sInstance = new DisplayInfoManager(context);
+ }
+ }
+ }
+ return sInstance;
+ }
+
+ /**
+ * Test purpose only. To release the instance so that the test can create a new instance.
+ */
+ @VisibleForTesting
+ static void releaseInstance() {
+ sInstance = null;
+ }
+
+ /**
+ * Update the preview size according to current display size.
+ */
+ void refresh() {
+ mPreviewSize = calculatePreviewSize();
+ }
+
+ /**
+ * Retrieves the display which has the max size among all displays.
+ */
+ @SuppressWarnings("deprecation") /* getRealSize */
+ @NonNull
+ Display getMaxSizeDisplay() {
+ Display[] displays = mDisplayManager.getDisplays();
+ if (displays.length == 1) {
+ return displays[0];
+ }
+
+ Display maxDisplay = null;
+ int maxDisplaySize = -1;
+ for (Display display : displays) {
+ Point displaySize = new Point();
+ display.getRealSize(displaySize);
+ if (displaySize.x * displaySize.y > maxDisplaySize) {
+ maxDisplaySize = displaySize.x * displaySize.y;
+ maxDisplay = display;
+ }
+ }
+
+ if (maxDisplay == null) {
+ throw new IllegalArgumentException("No display can be found from the input display "
+ + "manager!");
+ }
+ return maxDisplay;
+ }
+
+ /**
+ * PREVIEW refers to the best size match to the device's screen resolution, or to 1080p
+ * (1920x1080), whichever is smaller.
+ */
+ @NonNull
+ Size getPreviewSize() {
+ // Use cached value to speed up since this would be called multiple times.
+ if (mPreviewSize != null) {
+ return mPreviewSize;
+ }
+
+ mPreviewSize = calculatePreviewSize();
+ return mPreviewSize;
+ }
+
+ @SuppressWarnings("deprecation") /* getRealSize */
+ private Size calculatePreviewSize() {
+ Point displaySize = new Point();
+ Display display = getMaxSizeDisplay();
+ display.getRealSize(displaySize);
+ Size displayViewSize;
+ if (displaySize.x > displaySize.y) {
+ displayViewSize = new Size(displaySize.x, displaySize.y);
+ } else {
+ displayViewSize = new Size(displaySize.y, displaySize.x);
+ }
+
+ if (displayViewSize.getWidth() * displayViewSize.getHeight()
+ > MAX_PREVIEW_SIZE.getWidth() * MAX_PREVIEW_SIZE.getHeight()) {
+ return MAX_PREVIEW_SIZE;
+ } else {
+ return displayViewSize;
+ }
+ }
+}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/DisplayUtil.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/DisplayUtil.java
deleted file mode 100644
index 11f7b14..0000000
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/DisplayUtil.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.camera.camera2.internal;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.hardware.display.DisplayManager;
-import android.view.Display;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-
-/**
- * Utility class for {@link Display} related information.
- */
-@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-public final class DisplayUtil {
- /**
- * Retrieves the display which has the max size among all displays.
- */
- @SuppressWarnings("deprecation") /* getRealSize */
- @NonNull
- public static Display getMaxSizeDisplay(@NonNull DisplayManager displayManager) {
- Display maxDisplay = null;
- int maxDisplaySize = -1;
-
- for (Display display : displayManager.getDisplays()) {
- Point displaySize = new Point();
- display.getRealSize(displaySize);
- if (displaySize.x * displaySize.y > maxDisplaySize) {
- maxDisplaySize = displaySize.x * displaySize.y;
- maxDisplay = display;
- }
- }
-
- if (maxDisplay == null) {
- throw new IllegalArgumentException("No display can be found from the input display "
- + "manager!");
- }
-
- return maxDisplay;
- }
-
- /**
- * Retrieves the {@link DisplayManager} instance.
- */
- @NonNull
- public static DisplayManager getDisplayManager(@NonNull Context context) {
- return (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
- }
-
- private DisplayUtil() {}
-}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
index be5dfad..e387700 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
@@ -18,11 +18,9 @@
import android.content.Context;
import android.graphics.ImageFormat;
-import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.params.StreamConfigurationMap;
-import android.hardware.display.DisplayManager;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Build;
@@ -53,6 +51,7 @@
import androidx.camera.core.impl.SurfaceSizeDefinition;
import androidx.camera.core.impl.UseCaseConfig;
import androidx.camera.core.impl.utils.CameraOrientationUtil;
+import androidx.camera.core.impl.utils.CompareSizesByArea;
import androidx.core.util.Preconditions;
import java.util.ArrayList;
@@ -75,7 +74,6 @@
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
final class SupportedSurfaceCombination {
private static final String TAG = "SupportedSurfaceCombination";
- private static final Size MAX_PREVIEW_SIZE = new Size(1920, 1080);
private static final Size DEFAULT_SIZE = new Size(640, 480);
private static final Size ZERO_SIZE = new Size(0, 0);
private static final Size QUALITY_1080P_SIZE = new Size(1920, 1080);
@@ -100,6 +98,8 @@
private boolean mIsBurstCaptureSupported = false;
private SurfaceSizeDefinition mSurfaceSizeDefinition;
private Map<Integer, Size[]> mOutputSizesCache = new HashMap<>();
+ @NonNull
+ private final DisplayInfoManager mDisplayInfoManager;
SupportedSurfaceCombination(@NonNull Context context, @NonNull String cameraId,
@NonNull CameraManagerCompat cameraManagerCompat,
@@ -110,6 +110,7 @@
mExcludedSupportedSizesContainer = new ExcludedSupportedSizesContainer(cameraId);
mExtraSupportedSurfaceCombinationsContainer =
new ExtraSupportedSurfaceCombinationsContainer();
+ mDisplayInfoManager = DisplayInfoManager.getInstance(context);
try {
mCharacteristics = cameraManagerCompat.getCameraCharacteristicsCompat(mCameraId);
@@ -122,7 +123,7 @@
throw CameraUnavailableExceptionHelper.createFrom(e);
}
generateSupportedCombinationList();
- generateSurfaceSizeDefinition(DisplayUtil.getDisplayManager(context));
+ generateSurfaceSizeDefinition();
checkCustomization();
}
@@ -222,6 +223,25 @@
Map<UseCaseConfig<?>, Size> getSuggestedResolutions(
@NonNull List<SurfaceConfig> existingSurfaces,
@NonNull List<UseCaseConfig<?>> newUseCaseConfigs) {
+ // Refresh Preview Size based on current display configurations.
+ refreshPreviewSize();
+
+ // Use the small size (640x480) for new use cases to check whether there is any possible
+ // supported combination first
+ List<SurfaceConfig> surfaceConfigs = new ArrayList<>(existingSurfaces);
+ for (UseCaseConfig<?> useCaseConfig : newUseCaseConfigs) {
+ surfaceConfigs.add(
+ transformSurfaceConfig(useCaseConfig.getInputFormat(),
+ new Size(640, 480)));
+ }
+
+ if (!checkSupported(surfaceConfigs)) {
+ throw new IllegalArgumentException(
+ "No supported surface combination is found for camera device - Id : "
+ + mCameraId + ". May be attempting to bind too many use cases. "
+ + "Existing surfaces: " + existingSurfaces + " New configs: "
+ + newUseCaseConfigs);
+ }
// Get the index order list by the use case priority for finding stream configuration
List<Integer> useCasesPriorityOrder = getUseCasesPriorityOrder(newUseCaseConfigs);
@@ -322,6 +342,7 @@
return outputRatio;
}
+ @VisibleForTesting
SurfaceSizeDefinition getSurfaceSizeDefinition() {
return mSurfaceSizeDefinition;
}
@@ -1189,40 +1210,25 @@
// Utility classes and methods:
// *********************************************************************************************
- private void generateSurfaceSizeDefinition(@NonNull DisplayManager displayManager) {
+ private void generateSurfaceSizeDefinition() {
Size analysisSize = new Size(640, 480);
- Size previewSize = getPreviewSize(displayManager);
+ Size previewSize = mDisplayInfoManager.getPreviewSize();
Size recordSize = getRecordSize();
mSurfaceSizeDefinition =
SurfaceSizeDefinition.create(analysisSize, previewSize, recordSize);
}
- /**
- * PREVIEW refers to the best size match to the device's screen resolution, or to 1080p
- * (1920x1080), whichever is smaller.
- */
- @SuppressWarnings("deprecation") /* getRealSize */
- @NonNull
- static Size getPreviewSize(@NonNull DisplayManager displayManager) {
- Point displaySize = new Point();
- DisplayUtil.getMaxSizeDisplay(displayManager).getRealSize(displaySize);
-
- Size displayViewSize;
- if (displaySize.x > displaySize.y) {
- displayViewSize = new Size(displaySize.x, displaySize.y);
+ private void refreshPreviewSize() {
+ mDisplayInfoManager.refresh();
+ if (mSurfaceSizeDefinition == null) {
+ generateSurfaceSizeDefinition();
} else {
- displayViewSize = new Size(displaySize.y, displaySize.x);
+ Size previewSize = mDisplayInfoManager.getPreviewSize();
+ mSurfaceSizeDefinition = SurfaceSizeDefinition.create(
+ mSurfaceSizeDefinition.getAnalysisSize(),
+ previewSize,
+ mSurfaceSizeDefinition.getRecordSize());
}
-
- // Limit the max preview size to under min(display size, 1080P) by comparing the area size
- Size previewSize =
- Collections.min(
- Arrays.asList(
- new Size(displayViewSize.getWidth(), displayViewSize.getHeight()),
- MAX_PREVIEW_SIZE),
- new CompareSizesByArea());
-
- return previewSize;
}
/**
@@ -1334,34 +1340,6 @@
return excludedSizes;
}
- /** Comparator based on area of the given {@link Size} objects. */
- @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
- static final class CompareSizesByArea implements Comparator<Size> {
- private boolean mReverse = false;
-
- CompareSizesByArea() {
- }
-
- CompareSizesByArea(boolean reverse) {
- mReverse = reverse;
- }
-
- @Override
- public int compare(Size lhs, Size rhs) {
- // We cast here to ensure the multiplications won't overflow
- int result =
- Long.signum(
- (long) lhs.getWidth() * lhs.getHeight()
- - (long) rhs.getWidth() * rhs.getHeight());
-
- if (mReverse) {
- result *= -1;
- }
-
- return result;
- }
- }
-
/** Comparator based on how close they are to the target aspect ratio. */
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
static final class CompareAspectRatiosByDistanceToTargetRatio implements Comparator<Rational> {
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraFactoryTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraFactoryTest.kt
new file mode 100644
index 0000000..2c6b7f2
--- /dev/null
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2CameraFactoryTest.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.internal
+
+import android.content.Context
+import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraMetadata
+import android.os.Build
+import android.os.Handler
+import android.os.Looper
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.impl.CameraThreadConfig
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadow.api.Shadow
+import org.robolectric.shadows.ShadowCameraCharacteristics
+import org.robolectric.shadows.ShadowCameraManager
+import org.robolectric.util.ReflectionHelpers
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(
+ minSdk = Build.VERSION_CODES.LOLLIPOP,
+ instrumentedPackages = ["androidx.camera.camera2.internal"]
+)
+class Camera2CameraFactoryTest {
+ @Test
+ fun filterOutIncompatibleCameras_withoutAvailableCameraSelector() {
+ // Customizes Build.FINGERPRINT to be not "fingerprint", so that cameras without
+ // REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE will be filtered.
+ ReflectionHelpers.setStaticField(Build::class.java, "FINGERPRINT", "fake-fingerprint")
+
+ setupCameras()
+
+ val camera2CameraFactory = Camera2CameraFactory(
+ ApplicationProvider.getApplicationContext(),
+ CameraThreadConfig.create(
+ CameraXExecutors.mainThreadExecutor(),
+ Handler(Looper.getMainLooper())
+ ),
+ null)
+
+ assertThat(camera2CameraFactory.availableCameraIds).containsExactly("0", "1", "2")
+ }
+
+ @Test
+ fun filterOutIncompatibleCameras_withAvailableCameraSelector() {
+ // Customizes Build.FINGERPRINT to be not "fingerprint", so that cameras without
+ // REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE will be filtered.
+ ReflectionHelpers.setStaticField(Build::class.java, "FINGERPRINT", "fake-fingerprint")
+
+ setupCameras()
+
+ val camera2CameraFactory = Camera2CameraFactory(
+ ApplicationProvider.getApplicationContext(),
+ CameraThreadConfig.create(
+ CameraXExecutors.mainThreadExecutor(),
+ Handler(Looper.getMainLooper())
+ ),
+ CameraSelector.DEFAULT_BACK_CAMERA)
+
+ assertThat(camera2CameraFactory.availableCameraIds).containsExactly("0", "2")
+ }
+
+ @Test
+ fun NotFilterOutIncompatibleCameras_whenBuildFingerprintIsRobolectric() {
+ setupCameras()
+
+ val camera2CameraFactory = Camera2CameraFactory(
+ ApplicationProvider.getApplicationContext(),
+ CameraThreadConfig.create(
+ CameraXExecutors.mainThreadExecutor(),
+ Handler(Looper.getMainLooper())
+ ),
+ null)
+
+ assertThat(camera2CameraFactory.availableCameraIds).containsExactly("0", "1", "2", "3")
+ }
+
+ private fun setupCameras() {
+ val capabilities =
+ intArrayOf(CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)
+ // Camera "0" and "1" won't be filtered out even they don't have
+ // REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE.
+ initCharacterisic("0", CameraCharacteristics.LENS_FACING_BACK, null)
+ initCharacterisic("1", CameraCharacteristics.LENS_FACING_FRONT, null)
+ initCharacterisic("2", CameraCharacteristics.LENS_FACING_BACK, capabilities)
+ // Do not set REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE for the camera, so that
+ // it will be filtered out if Build.FINGERPRINT is no "robolectric".
+ // Do not set REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE for the camera, so that
+ // it will be filtered out if Build.FINGERPRINT is no "robolectric".
+ initCharacterisic("3", CameraCharacteristics.LENS_FACING_BACK, null)
+ }
+
+ private fun initCharacterisic(cameraId: String, lensFacing: Int, capabilities: IntArray?) {
+ val characteristics = ShadowCameraCharacteristics.newCameraCharacteristics()
+ val shadowCharacteristics = Shadow.extract<ShadowCameraCharacteristics>(characteristics)
+ shadowCharacteristics.set(
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
+ )
+
+ // Add a lens facing to the camera
+ shadowCharacteristics.set(CameraCharacteristics.LENS_FACING, lensFacing)
+
+ capabilities?.let {
+ shadowCharacteristics.set(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES,
+ capabilities
+ )
+ }
+
+ // Add the camera to the camera service
+ (Shadow.extract<Any>(
+ ApplicationProvider.getApplicationContext<Context>()
+ .getSystemService(Context.CAMERA_SERVICE)
+ ) as ShadowCameraManager)
+ .addCamera(cameraId, characteristics)
+ }
+}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
index 0720e88..51582d9 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/Camera2DeviceSurfaceManagerTest.java
@@ -138,6 +138,7 @@
@Before
@SuppressWarnings("deprecation") /* defaultDisplay */
public void setUp() throws IllegalAccessException {
+ DisplayInfoManager.releaseInstance();
WindowManager windowManager =
(WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Shadows.shadowOf(windowManager.getDefaultDisplay()).setRealWidth(mDisplaySize.getWidth());
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/DisplayInfoManagerTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/DisplayInfoManagerTest.kt
new file mode 100644
index 0000000..2815d19
--- /dev/null
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/DisplayInfoManagerTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.internal
+
+import android.content.Context
+import android.graphics.Point
+import android.hardware.display.DisplayManager
+import android.os.Build
+import android.util.Size
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.shadows.ShadowDisplayManager
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+@Suppress("DEPRECATION") // getRealSize
+class DisplayInfoManagerTest {
+ private fun addDisplay(width: Int, height: Int) {
+ val displayStr = String.format("w%ddp-h%ddp", width, height)
+ ShadowDisplayManager.addDisplay(displayStr)
+ }
+
+ @After
+ fun tearDown() {
+ DisplayInfoManager.releaseInstance()
+ }
+
+ @Test
+ fun canReturnMaxSizeDisplay_oneDisplay() {
+ // Arrange
+ val displayManager = (ApplicationProvider.getApplicationContext() as Context)
+ .getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ val currentDisplaySize = Point()
+ displayManager.displays.get(0).getRealSize(currentDisplaySize)
+
+ // Act
+ val displayInfoManager = DisplayInfoManager
+ .getInstance(ApplicationProvider.getApplicationContext())
+ val size = Point()
+ displayInfoManager.maxSizeDisplay.getRealSize(size)
+
+ // Assert
+ assertThat(size).isEqualTo(currentDisplaySize)
+ }
+
+ @Test
+ fun canReturnMaxSizeDisplay_multipleDisplay() {
+ // Arrange
+ addDisplay(2000, 3000)
+ addDisplay(480, 640)
+
+ // Act
+ val displayInfoManager = DisplayInfoManager
+ .getInstance(ApplicationProvider.getApplicationContext())
+ val size = Point()
+ displayInfoManager.maxSizeDisplay.getRealSize(size)
+
+ // Assert
+ assertThat(size).isEqualTo(Point(2000, 3000))
+ }
+
+ @Test
+ fun canReturnPreviewSize_displaySmallerThan1080P() {
+ // Arrange
+ addDisplay(480, 640)
+
+ // Act & Assert
+ val displayInfoManager = DisplayInfoManager
+ .getInstance(ApplicationProvider.getApplicationContext())
+ assertThat(displayInfoManager.previewSize).isEqualTo(Size(640, 480))
+ }
+
+ @Test
+ fun canReturnPreviewSize_displayLargerThan1080P() {
+ // Arrange
+ addDisplay(2000, 3000)
+
+ // Act & Assert
+ val displayInfoManager = DisplayInfoManager
+ .getInstance(ApplicationProvider.getApplicationContext())
+ assertThat(displayInfoManager.previewSize).isEqualTo(Size(1920, 1080))
+ }
+
+ @Test
+ fun canReturnDifferentPreviewSize_refreshIsCalled() {
+ // Arrange
+ val displayInfoManager = spy(
+ DisplayInfoManager
+ .getInstance(ApplicationProvider.getApplicationContext())
+ )
+
+ // Act
+ displayInfoManager.previewSize
+ displayInfoManager.refresh()
+ displayInfoManager.previewSize
+
+ // Assert
+ verify(displayInfoManager, times(2)).maxSizeDisplay
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
index 3561b59..d632a57 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.java
@@ -61,6 +61,7 @@
import androidx.camera.core.impl.SurfaceConfig.ConfigType;
import androidx.camera.core.impl.UseCaseConfig;
import androidx.camera.core.impl.UseCaseConfigFactory;
+import androidx.camera.core.impl.utils.CompareSizesByArea;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.internal.CameraUseCaseAdapter;
import androidx.camera.testing.CameraUtil;
@@ -169,6 +170,7 @@
@Before
@SuppressWarnings("deprecation") /* defaultDisplay */
public void setUp() throws IllegalAccessException {
+ DisplayInfoManager.releaseInstance();
WindowManager windowManager =
(WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Shadows.shadowOf(windowManager.getDefaultDisplay()).setRealWidth(mDisplaySize.getWidth());
@@ -1204,7 +1206,7 @@
}
// The testing sizes array will be equal to mSupportedSizes after sorting.
- Arrays.sort(sizes, new SupportedSurfaceCombination.CompareSizesByArea(true));
+ Arrays.sort(sizes, new CompareSizesByArea(true));
assertThat(Arrays.asList(sizes)).isEqualTo(Arrays.asList(mSupportedSizes));
}
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index 0d05b94..2e2f826 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -106,5 +106,9 @@
description = "Core components for the Jetpack Camera Library, a library providing a " +
"consistent and reliable camera foundation that enables great camera driven " +
"experiences across all of Android."
-}
+ license {
+ name = "BSD License"
+ url = "https://chromium.googlesource.com/libyuv/libyuv/+/refs/heads/main/README.chromium"
+ }
+}
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/MetadataImageReaderTest.java b/camera/camera-core/src/androidTest/java/androidx/camera/core/MetadataImageReaderTest.java
index 8a0a68f..2bd8d10 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/MetadataImageReaderTest.java
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/MetadataImageReaderTest.java
@@ -253,15 +253,11 @@
final AtomicReference<ImageProxy> receivedImage = new AtomicReference<>();
- ImageReaderProxy.OnImageAvailableListener outputListener =
- new ImageReaderProxy.OnImageAvailableListener() {
- @Override
- public void onImageAvailable(@NonNull ImageReaderProxy imageReader) {
+ ImageReaderProxy.OnImageAvailableListener outputListener = imageReader -> {
// The First ImageProxy is output without closing.
ImageProxy resultImage = imageReader.acquireNextImage();
receivedImage.set(resultImage);
mSemaphore.release();
- }
};
mMetadataImageReader.setOnImageAvailableListener(outputListener, mBackgroundExecutor);
// Feeds the first Image.
@@ -271,25 +267,19 @@
assertThat(receivedImage.get().getImageInfo().getTimestamp()).isEqualTo(
TIMESTAMP_0);
- final AtomicBoolean hasReceivedSecondImage = new AtomicBoolean(false);
- outputListener =
- new ImageReaderProxy.OnImageAvailableListener() {
- @Override
- public void onImageAvailable(@NonNull ImageReaderProxy imageReader) {
- // The second ImageProxy should be dropped, otherwise fail the test case.
- hasReceivedSecondImage.set(true);
- mSemaphore.release();
- }
- };
+ Semaphore secondSemaphore = new Semaphore(0);
+ outputListener = imageReader -> {
+ // The second ImageProxy should be dropped, otherwise fail the test case.
+ secondSemaphore.release();
+ };
mMetadataImageReader.setOnImageAvailableListener(outputListener, mBackgroundExecutor);
// Feeds the second Image.
TagBundle tagBundle = TagBundle.create(new Pair<>("FakeCaptureStageId", 0));
- assertThat(mImageReader.triggerImageAvailable(tagBundle, TIMESTAMP_1, 50,
- TimeUnit.MILLISECONDS)).isFalse();
-
+ mImageReader.triggerImageAvailable(tagBundle, TIMESTAMP_1);
HandlerUtil.waitForLooperToIdle(mBackgroundHandler);
- assertThat(hasReceivedSecondImage.get()).isFalse();
+ // OnImageAvailableListener won't be called for the 2nd image.
+ assertThat(secondSemaphore.tryAcquire(1, TimeUnit.SECONDS)).isFalse();
}
@Test
diff --git a/camera/camera-core/src/main/cpp/image_processing_util_jni.cc b/camera/camera-core/src/main/cpp/image_processing_util_jni.cc
index 253ff25..a8b033e 100644
--- a/camera/camera-core/src/main/cpp/image_processing_util_jni.cc
+++ b/camera/camera-core/src/main/cpp/image_processing_util_jni.cc
@@ -357,14 +357,14 @@
// NV21
result = libyuv::NV12ToI420Rotate(src_y_ptr,
src_stride_y,
- src_u_ptr,
- src_stride_u,
+ src_v_ptr,
+ src_stride_v,
rotated_y_ptr,
rotated_stride_y,
- rotated_u_ptr,
- rotated_stride_u,
rotated_v_ptr,
rotated_stride_v,
+ rotated_u_ptr,
+ rotated_stride_u,
width,
height,
mode);
@@ -372,8 +372,8 @@
// NV12
result = libyuv::NV12ToI420Rotate(src_y_ptr,
src_stride_y,
- src_v_ptr,
- src_stride_v,
+ src_u_ptr,
+ src_stride_u,
rotated_y_ptr,
rotated_stride_y,
rotated_u_ptr,
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CamcorderProfileProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CamcorderProfileProxy.java
index 3c37a1b..325c32f 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CamcorderProfileProxy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CamcorderProfileProxy.java
@@ -16,13 +16,34 @@
package androidx.camera.core.impl;
-import android.media.CamcorderProfile;
+import static android.media.MediaRecorder.AudioEncoder.AAC;
+import static android.media.MediaRecorder.AudioEncoder.AAC_ELD;
+import static android.media.MediaRecorder.AudioEncoder.AMR_NB;
+import static android.media.MediaRecorder.AudioEncoder.AMR_WB;
+import static android.media.MediaRecorder.AudioEncoder.HE_AAC;
+import static android.media.MediaRecorder.AudioEncoder.OPUS;
+import static android.media.MediaRecorder.AudioEncoder.VORBIS;
+import static android.media.MediaRecorder.VideoEncoder.H263;
+import static android.media.MediaRecorder.VideoEncoder.H264;
+import static android.media.MediaRecorder.VideoEncoder.HEVC;
+import static android.media.MediaRecorder.VideoEncoder.MPEG_4_SP;
+import static android.media.MediaRecorder.VideoEncoder.VP8;
+import android.media.CamcorderProfile;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.media.MediaRecorder;
+
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.auto.value.AutoValue;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* CamcorderProfileProxy defines the get methods that is mapping to the fields of
* {@link CamcorderProfile}.
@@ -31,17 +52,31 @@
@AutoValue
public abstract class CamcorderProfileProxy {
+ /** Constant representing no codec profile. */
+ public static int CODEC_PROFILE_NONE = -1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({H263, H264, HEVC, VP8, MPEG_4_SP, MediaRecorder.VideoEncoder.DEFAULT})
+ @interface VideoEncoder {
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({AAC, AAC_ELD, AMR_NB, AMR_WB, HE_AAC, OPUS, VORBIS,
+ MediaRecorder.AudioEncoder.DEFAULT})
+ @interface AudioEncoder {
+ }
+
/** Creates a CamcorderProfileProxy instance. */
@NonNull
public static CamcorderProfileProxy create(int duration,
int quality,
int fileFormat,
- int videoCodec,
+ @VideoEncoder int videoCodec,
int videoBitRate,
int videoFrameRate,
int videoFrameWidth,
int videoFrameHeight,
- int audioCodec,
+ @AudioEncoder int audioCodec,
int audioBitRate,
int audioSampleRate,
int audioChannels) {
@@ -91,6 +126,7 @@
public abstract int getFileFormat();
/** @see CamcorderProfile#videoCodec */
+ @VideoEncoder
public abstract int getVideoCodec();
/** @see CamcorderProfile#videoBitRate */
@@ -106,6 +142,7 @@
public abstract int getVideoFrameHeight();
/** @see CamcorderProfile#audioCodec */
+ @AudioEncoder
public abstract int getAudioCodec();
/** @see CamcorderProfile#audioBitRate */
@@ -116,5 +153,89 @@
/** @see CamcorderProfile#audioChannels */
public abstract int getAudioChannels();
-}
+ /**
+ * Returns a mime-type string for the video codec type returned by {@link #getVideoCodec()}.
+ *
+ * @return A mime-type string or {@code null} if the codec type is
+ * {@link android.media.MediaRecorder.VideoEncoder#DEFAULT}, as this type is under-defined
+ * and cannot be resolved to a specific mime type without more information.
+ */
+ @Nullable
+ public String getVideoCodecMimeType() {
+ // Mime-type definitions taken from
+ // frameworks/av/media/libstagefright/foundation/MediaDefs.cpp
+ switch (getVideoCodec()) {
+ case H263:
+ return MediaFormat.MIMETYPE_VIDEO_H263;
+ case H264:
+ return MediaFormat.MIMETYPE_VIDEO_AVC;
+ case HEVC:
+ return MediaFormat.MIMETYPE_VIDEO_HEVC;
+ case VP8:
+ return MediaFormat.MIMETYPE_VIDEO_VP8;
+ case MPEG_4_SP:
+ return MediaFormat.MIMETYPE_VIDEO_MPEG4;
+ case MediaRecorder.VideoEncoder.DEFAULT:
+ break;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a mime-type string for the audio codec type returned by {@link #getAudioCodec()}.
+ *
+ * @return A mime-type string or {@code null} if the codec type is
+ * {@link android.media.MediaRecorder.AudioEncoder#DEFAULT}, as this type is under-defined
+ * and cannot be resolved to a specific mime type without more information.
+ */
+ @Nullable
+ public String getAudioCodecMimeType() {
+ // Mime-type definitions taken from
+ // frameworks/av/media/libstagefright/foundation/MediaDefs.cpp
+ switch (getAudioCodec()) {
+ case AAC: // Should use aac-profile LC
+ case HE_AAC: // Should use aac-profile HE
+ case AAC_ELD: // Should use aac-profile ELD
+ return MediaFormat.MIMETYPE_AUDIO_AAC;
+ case AMR_NB:
+ return MediaFormat.MIMETYPE_AUDIO_AMR_NB;
+ case AMR_WB:
+ return MediaFormat.MIMETYPE_AUDIO_AMR_WB;
+ case OPUS:
+ return MediaFormat.MIMETYPE_AUDIO_OPUS;
+ case VORBIS:
+ return MediaFormat.MIMETYPE_AUDIO_VORBIS;
+ case MediaRecorder.AudioEncoder.DEFAULT:
+ break;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the required audio profile for the audio encoder given by {@link #getAudioCodec()}.
+ *
+ * <p>For example, this can be used to differentiate between AAC encoders
+ * {@link android.media.MediaRecorder.AudioEncoder#AAC},
+ * {@link android.media.MediaRecorder.AudioEncoder#AAC_ELD},
+ * and {@link android.media.MediaRecorder.AudioEncoder#HE_AAC}.
+ * Should be used with the {@link MediaCodecInfo.CodecProfileLevel#profile} field.
+ *
+ * @return The profile required by the audio codec. If no profile is required, returns
+ * {@link #CODEC_PROFILE_NONE}.
+ */
+ public int getRequiredAudioProfile() {
+ switch (getAudioCodec()) {
+ case AAC:
+ return MediaCodecInfo.CodecProfileLevel.AACObjectLC;
+ case AAC_ELD:
+ return MediaCodecInfo.CodecProfileLevel.AACObjectELD;
+ case HE_AAC:
+ return MediaCodecInfo.CodecProfileLevel.AACObjectHE;
+ default:
+ return CODEC_PROFILE_NONE;
+ }
+ }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/CompareSizesByArea.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/CompareSizesByArea.java
new file mode 100644
index 0000000..a5ce6a8
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/CompareSizesByArea.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.core.impl.utils;
+
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import java.util.Comparator;
+
+/** Comparator based on area of the given {@link Size} objects. */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public final class CompareSizesByArea implements Comparator<Size> {
+ private boolean mReverse = false;
+
+ /** Creates a comparator with standard total ordering. */
+ public CompareSizesByArea() {
+ this(false);
+ }
+
+ /** Creates a comparator which can reverse the total ordering. */
+ public CompareSizesByArea(boolean reverse) {
+ mReverse = reverse;
+ }
+
+ @Override
+ public int compare(@NonNull Size lhs, @NonNull Size rhs) {
+ // We cast here to ensure the multiplications won't overflow
+ int result =
+ Long.signum(
+ (long) lhs.getWidth() * lhs.getHeight()
+ - (long) rhs.getWidth() * rhs.getHeight());
+
+ if (mReverse) {
+ result *= -1;
+ }
+
+ return result;
+ }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
index 7423901..4f9bacc 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
@@ -24,6 +24,7 @@
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.SystemClock;
import android.util.Pair;
import android.view.Surface;
@@ -53,6 +54,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -256,6 +258,64 @@
.containsExactly(TIMESTAMP_1, TIMESTAMP_2, TIMESTAMP_3);
}
+ /*
+ * Verify that ImageAnalysis#setAnalyzer won't cause any image leakage.
+ */
+ @Test
+ public void analyzerSetMultipleTimesInKeepOnlyLatestMode() throws InterruptedException,
+ CameraUseCaseAdapter.CameraException {
+ setUpImageAnalysisWithStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST);
+
+ assertCanReceiveAnalysisImage(mImageAnalysis);
+
+ List<ImageProxy> postedImageProxies = new ArrayList<>();
+ ImageAnalysis.Analyzer slowImageAnalyzer = image -> {
+ // image left as unclosed until we closed all images in postedImageProxies.
+ postedImageProxies.add(image);
+ };
+
+ mImageAnalysis.setAnalyzer(CameraXExecutors.directExecutor(), slowImageAnalyzer);
+ triggerNextImage(); // +1 unclosed image (mPostedImage)
+ triggerNextImage(); // +1 unclosed image (mCachedImage) if the image leakage happens.
+
+ // If setAnalysis does thing inappropriately(e.g, clear mCachedImage reference without
+ // closing it), previous unclosed image(mCachedImage) won't be closed.
+ mImageAnalysis.setAnalyzer(CameraXExecutors.directExecutor(), slowImageAnalyzer);
+ triggerNextImage(); //+1 unclosed image (mCachedImage) if the image leakage happens.
+
+ mImageAnalysis.setAnalyzer(CameraXExecutors.directExecutor(), slowImageAnalyzer);
+ triggerNextImage(); // +1 unclosed image (mCachedImage) if the image leakage happens.
+
+ mImageAnalysis.setAnalyzer(CameraXExecutors.directExecutor(), slowImageAnalyzer);
+ triggerNextImage(); // +1 unclosed image (mCachedImage) if the image leakage happens.
+ // If unclosed image reaches 5 (MaxImages == 4), it will throw
+ // IllegalStateException(MaxImages) and no image can be received.
+
+ // Closed all posted ImageProxies to ensure the analyzer can receive ImageProxy normally.
+ for (ImageProxy imagePendingProxy : postedImageProxies) {
+ imagePendingProxy.close();
+ }
+
+ // If image leakage happens, 4 unclosed image will never be closed. It means the analyzer
+ // won't be able to receive images anymore.
+ assertCanReceiveAnalysisImage(mImageAnalysis);
+ }
+
+ void assertCanReceiveAnalysisImage(ImageAnalysis imageAnalysis) throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ imageAnalysis.setAnalyzer(CameraXExecutors.directExecutor(), image -> {
+ image.close();
+ latch.countDown();
+ });
+ triggerNextImage();
+ assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
+ }
+
+ void triggerNextImage() throws InterruptedException {
+ mFakeImageReaderProxy.triggerImageAvailable(mTagBundle, SystemClock.elapsedRealtime());
+ flushHandler(mBackgroundHandler);
+ }
+
private void setUpImageAnalysisWithStrategy(
@ImageAnalysis.BackpressureStrategy int backpressureStrategy) throws
CameraUseCaseAdapter.CameraException {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageReaderProxy.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageReaderProxy.java
index da94576..29c6e3e 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageReaderProxy.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeImageReaderProxy.java
@@ -35,7 +35,6 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
/**
* A fake implementation of ImageReaderProxy where the values are settable and the
@@ -77,7 +76,10 @@
*/
public FakeImageReaderProxy(int maxImages) {
mMaxImages = maxImages;
- mImageProxyBlockingQueue = new LinkedBlockingQueue<>(maxImages);
+ // Allows more image to be produced than what can be resumed(acquired) to align with the
+ // actual ImageReader behavior. It means triggerImageAvailable can still succeed even when
+ // acquired images reaches maxImages and all are not closed.
+ mImageProxyBlockingQueue = new LinkedBlockingQueue<>(maxImages + 2);
mImageProxyAcquisitionQueue = new LinkedBlockingQueue<>(maxImages);
}
@@ -96,6 +98,7 @@
return fakeImageReaderProxy;
}
+ @Nullable
@Override
public ImageProxy acquireLatestImage() {
ImageProxy imageProxy = null;
@@ -113,11 +116,12 @@
"Unable to acquire latest image from empty FakeImageReader");
}
+ checkIfExceedMaxImages();
mOutboundImageProxy.add(imageProxy);
-
return imageProxy;
}
+ @Nullable
@Override
public ImageProxy acquireNextImage() {
ImageProxy imageProxy;
@@ -129,9 +133,18 @@
"Unable to acquire next image from empty FakeImageReader");
}
+ checkIfExceedMaxImages();
return imageProxy;
}
+ private void checkIfExceedMaxImages() {
+ if (mImageProxyBlockingQueue.size() > mMaxImages) {
+ throw new IllegalStateException("maxImages (" + mMaxImages
+ + ") has already been acquired, call #close before acquiring more.");
+
+ }
+ }
+
@Override
public void close() {
for (ImageProxy imageProxy : mOutboundImageProxy) {
@@ -213,37 +226,6 @@
triggerImageAvailableListener();
}
- /**
- * Manually trigger OnImageAvailableListener to notify the Image is ready. If unable to add an
- * {@link ImageProxy} after the timeout occurs then will return false.
- *
- * <p> Blocks until successfully added an ImageProxy. This can block if the maximum number of
- * ImageProxy have been triggered without a {@link #acquireLatestImage()} or {@link
- * #acquireNextImage()} being called.
- *
- * @return true if able to trigger the OnImageAvailableListener. Otherwise will return false if
- * it fails to trigger the callback after the timeout period.
- */
- public boolean triggerImageAvailable(@NonNull TagBundle tagBundle, long timestamp, long timeout,
- @NonNull TimeUnit timeUnit) throws InterruptedException {
- FakeImageProxy fakeImageProxy = generateFakeImageProxy(tagBundle, timestamp);
-
- final ListenableFuture<Void> future = fakeImageProxy.getCloseFuture();
- if (mImageProxyBlockingQueue.offer(future, timeout, timeUnit)) {
- future.addListener(() -> mImageProxyBlockingQueue.remove(future),
- CameraXExecutors.directExecutor()
- );
-
- mImageProxyAcquisitionQueue.put(fakeImageProxy);
-
- triggerImageAvailableListener();
-
- return true;
- }
-
- return false;
- }
-
private FakeImageProxy generateFakeImageProxy(TagBundle tagBundle, long timestamp) {
FakeImageInfo fakeImageInfo = new FakeImageInfo();
fakeImageInfo.setTag(tagBundle);
diff --git a/camera/camera-video/api/current.txt b/camera/camera-video/api/current.txt
index cca6186..94e5745 100644
--- a/camera/camera-video/api/current.txt
+++ b/camera/camera-video/api/current.txt
@@ -12,6 +12,13 @@
field public static final int AUDIO_STATE_SOURCE_SILENCED = 2; // 0x2
}
+ @RequiresApi(21) public class FallbackStrategy {
+ method public static androidx.camera.video.FallbackStrategy higherQualityOrLowerThan(androidx.camera.video.Quality);
+ method public static androidx.camera.video.FallbackStrategy higherQualityThan(androidx.camera.video.Quality);
+ method public static androidx.camera.video.FallbackStrategy lowerQualityOrHigherThan(androidx.camera.video.Quality);
+ method public static androidx.camera.video.FallbackStrategy lowerQualityThan(androidx.camera.video.Quality);
+ }
+
@RequiresApi(21) public final class FileDescriptorOutputOptions extends androidx.camera.video.OutputOptions {
method public long getFileSizeLimit();
method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
@@ -64,29 +71,23 @@
method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public androidx.camera.video.PendingRecording withAudioEnabled();
}
- @RequiresApi(21) public class QualitySelector {
- method public static androidx.camera.video.QualitySelector.Procedure firstTry(int);
- method public static android.util.Size? getResolution(androidx.camera.core.CameraInfo, int);
- method public static java.util.List<java.lang.Integer!> getSupportedQualities(androidx.camera.core.CameraInfo);
- method public static boolean isQualitySupported(androidx.camera.core.CameraInfo, int);
- method public static androidx.camera.video.QualitySelector of(int);
- method public static androidx.camera.video.QualitySelector of(int, int);
- field public static final int FALLBACK_STRATEGY_HIGHER = 1; // 0x1
- field public static final int FALLBACK_STRATEGY_LOWER = 3; // 0x3
- field public static final int FALLBACK_STRATEGY_STRICTLY_HIGHER = 2; // 0x2
- field public static final int FALLBACK_STRATEGY_STRICTLY_LOWER = 4; // 0x4
- field public static final int QUALITY_FHD = 6; // 0x6
- field public static final int QUALITY_HD = 5; // 0x5
- field public static final int QUALITY_HIGHEST = 1; // 0x1
- field public static final int QUALITY_LOWEST = 0; // 0x0
- field public static final int QUALITY_SD = 4; // 0x4
- field public static final int QUALITY_UHD = 8; // 0x8
+ @RequiresApi(21) public class Quality {
+ field public static final androidx.camera.video.Quality FHD;
+ field public static final androidx.camera.video.Quality HD;
+ field public static final androidx.camera.video.Quality HIGHEST;
+ field public static final androidx.camera.video.Quality LOWEST;
+ field public static final androidx.camera.video.Quality SD;
+ field public static final androidx.camera.video.Quality UHD;
}
- public static class QualitySelector.Procedure {
- method public androidx.camera.video.QualitySelector finallyTry(int);
- method public androidx.camera.video.QualitySelector finallyTry(int, int);
- method public androidx.camera.video.QualitySelector.Procedure thenTry(int);
+ @RequiresApi(21) public final class QualitySelector {
+ method public static androidx.camera.video.QualitySelector from(androidx.camera.video.Quality);
+ method public static androidx.camera.video.QualitySelector from(androidx.camera.video.Quality, androidx.camera.video.FallbackStrategy);
+ method public static androidx.camera.video.QualitySelector fromOrderedList(java.util.List<androidx.camera.video.Quality!>);
+ method public static androidx.camera.video.QualitySelector fromOrderedList(java.util.List<androidx.camera.video.Quality!>, androidx.camera.video.FallbackStrategy);
+ method public static android.util.Size? getResolution(androidx.camera.core.CameraInfo, androidx.camera.video.Quality);
+ method public static java.util.List<androidx.camera.video.Quality!> getSupportedQualities(androidx.camera.core.CameraInfo);
+ method public static boolean isQualitySupported(androidx.camera.core.CameraInfo, androidx.camera.video.Quality);
}
@RequiresApi(21) public final class Recorder implements androidx.camera.video.VideoOutput {
diff --git a/camera/camera-video/api/public_plus_experimental_current.txt b/camera/camera-video/api/public_plus_experimental_current.txt
index cca6186..94e5745 100644
--- a/camera/camera-video/api/public_plus_experimental_current.txt
+++ b/camera/camera-video/api/public_plus_experimental_current.txt
@@ -12,6 +12,13 @@
field public static final int AUDIO_STATE_SOURCE_SILENCED = 2; // 0x2
}
+ @RequiresApi(21) public class FallbackStrategy {
+ method public static androidx.camera.video.FallbackStrategy higherQualityOrLowerThan(androidx.camera.video.Quality);
+ method public static androidx.camera.video.FallbackStrategy higherQualityThan(androidx.camera.video.Quality);
+ method public static androidx.camera.video.FallbackStrategy lowerQualityOrHigherThan(androidx.camera.video.Quality);
+ method public static androidx.camera.video.FallbackStrategy lowerQualityThan(androidx.camera.video.Quality);
+ }
+
@RequiresApi(21) public final class FileDescriptorOutputOptions extends androidx.camera.video.OutputOptions {
method public long getFileSizeLimit();
method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
@@ -64,29 +71,23 @@
method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public androidx.camera.video.PendingRecording withAudioEnabled();
}
- @RequiresApi(21) public class QualitySelector {
- method public static androidx.camera.video.QualitySelector.Procedure firstTry(int);
- method public static android.util.Size? getResolution(androidx.camera.core.CameraInfo, int);
- method public static java.util.List<java.lang.Integer!> getSupportedQualities(androidx.camera.core.CameraInfo);
- method public static boolean isQualitySupported(androidx.camera.core.CameraInfo, int);
- method public static androidx.camera.video.QualitySelector of(int);
- method public static androidx.camera.video.QualitySelector of(int, int);
- field public static final int FALLBACK_STRATEGY_HIGHER = 1; // 0x1
- field public static final int FALLBACK_STRATEGY_LOWER = 3; // 0x3
- field public static final int FALLBACK_STRATEGY_STRICTLY_HIGHER = 2; // 0x2
- field public static final int FALLBACK_STRATEGY_STRICTLY_LOWER = 4; // 0x4
- field public static final int QUALITY_FHD = 6; // 0x6
- field public static final int QUALITY_HD = 5; // 0x5
- field public static final int QUALITY_HIGHEST = 1; // 0x1
- field public static final int QUALITY_LOWEST = 0; // 0x0
- field public static final int QUALITY_SD = 4; // 0x4
- field public static final int QUALITY_UHD = 8; // 0x8
+ @RequiresApi(21) public class Quality {
+ field public static final androidx.camera.video.Quality FHD;
+ field public static final androidx.camera.video.Quality HD;
+ field public static final androidx.camera.video.Quality HIGHEST;
+ field public static final androidx.camera.video.Quality LOWEST;
+ field public static final androidx.camera.video.Quality SD;
+ field public static final androidx.camera.video.Quality UHD;
}
- public static class QualitySelector.Procedure {
- method public androidx.camera.video.QualitySelector finallyTry(int);
- method public androidx.camera.video.QualitySelector finallyTry(int, int);
- method public androidx.camera.video.QualitySelector.Procedure thenTry(int);
+ @RequiresApi(21) public final class QualitySelector {
+ method public static androidx.camera.video.QualitySelector from(androidx.camera.video.Quality);
+ method public static androidx.camera.video.QualitySelector from(androidx.camera.video.Quality, androidx.camera.video.FallbackStrategy);
+ method public static androidx.camera.video.QualitySelector fromOrderedList(java.util.List<androidx.camera.video.Quality!>);
+ method public static androidx.camera.video.QualitySelector fromOrderedList(java.util.List<androidx.camera.video.Quality!>, androidx.camera.video.FallbackStrategy);
+ method public static android.util.Size? getResolution(androidx.camera.core.CameraInfo, androidx.camera.video.Quality);
+ method public static java.util.List<androidx.camera.video.Quality!> getSupportedQualities(androidx.camera.core.CameraInfo);
+ method public static boolean isQualitySupported(androidx.camera.core.CameraInfo, androidx.camera.video.Quality);
}
@RequiresApi(21) public final class Recorder implements androidx.camera.video.VideoOutput {
diff --git a/camera/camera-video/api/restricted_current.txt b/camera/camera-video/api/restricted_current.txt
index 028e269..83ddacd 100644
--- a/camera/camera-video/api/restricted_current.txt
+++ b/camera/camera-video/api/restricted_current.txt
@@ -12,6 +12,13 @@
field public static final int AUDIO_STATE_SOURCE_SILENCED = 2; // 0x2
}
+ @RequiresApi(21) public class FallbackStrategy {
+ method public static androidx.camera.video.FallbackStrategy higherQualityOrLowerThan(androidx.camera.video.Quality);
+ method public static androidx.camera.video.FallbackStrategy higherQualityThan(androidx.camera.video.Quality);
+ method public static androidx.camera.video.FallbackStrategy lowerQualityOrHigherThan(androidx.camera.video.Quality);
+ method public static androidx.camera.video.FallbackStrategy lowerQualityThan(androidx.camera.video.Quality);
+ }
+
@RequiresApi(21) public final class FileDescriptorOutputOptions extends androidx.camera.video.OutputOptions {
method public long getFileSizeLimit();
method public android.os.ParcelFileDescriptor getParcelFileDescriptor();
@@ -64,29 +71,23 @@
method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public androidx.camera.video.PendingRecording withAudioEnabled();
}
- @RequiresApi(21) public class QualitySelector {
- method public static androidx.camera.video.QualitySelector.Procedure firstTry(int);
- method public static android.util.Size? getResolution(androidx.camera.core.CameraInfo, int);
- method public static java.util.List<java.lang.Integer!> getSupportedQualities(androidx.camera.core.CameraInfo);
- method public static boolean isQualitySupported(androidx.camera.core.CameraInfo, int);
- method public static androidx.camera.video.QualitySelector of(int);
- method public static androidx.camera.video.QualitySelector of(int, int);
- field public static final int FALLBACK_STRATEGY_HIGHER = 1; // 0x1
- field public static final int FALLBACK_STRATEGY_LOWER = 3; // 0x3
- field public static final int FALLBACK_STRATEGY_STRICTLY_HIGHER = 2; // 0x2
- field public static final int FALLBACK_STRATEGY_STRICTLY_LOWER = 4; // 0x4
- field public static final int QUALITY_FHD = 6; // 0x6
- field public static final int QUALITY_HD = 5; // 0x5
- field public static final int QUALITY_HIGHEST = 1; // 0x1
- field public static final int QUALITY_LOWEST = 0; // 0x0
- field public static final int QUALITY_SD = 4; // 0x4
- field public static final int QUALITY_UHD = 8; // 0x8
+ @RequiresApi(21) public class Quality {
+ field public static final androidx.camera.video.Quality FHD;
+ field public static final androidx.camera.video.Quality HD;
+ field public static final androidx.camera.video.Quality HIGHEST;
+ field public static final androidx.camera.video.Quality LOWEST;
+ field public static final androidx.camera.video.Quality SD;
+ field public static final androidx.camera.video.Quality UHD;
}
- public static class QualitySelector.Procedure {
- method public androidx.camera.video.QualitySelector finallyTry(int);
- method public androidx.camera.video.QualitySelector finallyTry(int, int);
- method public androidx.camera.video.QualitySelector.Procedure thenTry(int);
+ @RequiresApi(21) public final class QualitySelector {
+ method public static androidx.camera.video.QualitySelector from(androidx.camera.video.Quality);
+ method public static androidx.camera.video.QualitySelector from(androidx.camera.video.Quality, androidx.camera.video.FallbackStrategy);
+ method public static androidx.camera.video.QualitySelector fromOrderedList(java.util.List<androidx.camera.video.Quality!>);
+ method public static androidx.camera.video.QualitySelector fromOrderedList(java.util.List<androidx.camera.video.Quality!>, androidx.camera.video.FallbackStrategy);
+ method public static android.util.Size? getResolution(androidx.camera.core.CameraInfo, androidx.camera.video.Quality);
+ method public static java.util.List<androidx.camera.video.Quality!> getSupportedQualities(androidx.camera.core.CameraInfo);
+ method public static boolean isQualitySupported(androidx.camera.core.CameraInfo, androidx.camera.video.Quality);
}
@RequiresApi(21) public final class Recorder implements androidx.camera.video.VideoOutput {
diff --git a/camera/camera-video/build.gradle b/camera/camera-video/build.gradle
index 51f13dfd..0fe8c7c 100644
--- a/camera/camera-video/build.gradle
+++ b/camera/camera-video/build.gradle
@@ -45,6 +45,7 @@
testImplementation(libs.truth)
testImplementation(libs.robolectric)
testImplementation(libs.mockitoCore)
+ testImplementation("androidx.core:core-ktx:1.1.0")
testImplementation(project(":camera:camera-testing"), {
exclude group: "androidx.camera", module: "camera-core"
})
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 90f132f..730c55d 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
@@ -135,14 +135,14 @@
val cameraInfo = cameraUseCaseAdapter.cameraInfo
val candidates = mutableSetOf<Size>().apply {
if (testName.methodName == "setFileSizeLimit") {
- QualitySelector.getResolution(cameraInfo, QualitySelector.QUALITY_FHD)
+ QualitySelector.getResolution(cameraInfo, Quality.FHD)
?.let { add(it) }
- QualitySelector.getResolution(cameraInfo, QualitySelector.QUALITY_HD)
+ QualitySelector.getResolution(cameraInfo, Quality.HD)
?.let { add(it) }
- QualitySelector.getResolution(cameraInfo, QualitySelector.QUALITY_SD)
+ QualitySelector.getResolution(cameraInfo, Quality.SD)
?.let { add(it) }
}
- QualitySelector.getResolution(cameraInfo, QualitySelector.QUALITY_LOWEST)
+ QualitySelector.getResolution(cameraInfo, Quality.LOWEST)
?.let { add(it) }
}
assumeTrue(candidates.isNotEmpty())
@@ -946,7 +946,7 @@
@Test
fun optionsOverridesDefaults() {
- val qualitySelector = QualitySelector.of(QualitySelector.QUALITY_HIGHEST)
+ val qualitySelector = QualitySelector.from(Quality.HIGHEST)
val recorder = Recorder.Builder()
.setQualitySelector(qualitySelector)
.build()
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
index 315b698..d74c267 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoCaptureDeviceTest.kt
@@ -30,7 +30,6 @@
import androidx.camera.testing.CameraUtil
import androidx.camera.testing.CameraXUtil
import androidx.camera.testing.GLUtil
-import androidx.camera.video.QualitySelector.QUALITY_LOWEST
import androidx.camera.video.VideoOutput.StreamState
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -145,7 +144,7 @@
val targetResolution = QualitySelector.getResolution(cameraInfo, quality)
val videoOutput = TestVideoOutput(
mediaSpec = MediaSpec.builder().configureVideo {
- it.setQualitySelector(QualitySelector.of(quality))
+ it.setQualitySelector(QualitySelector.from(quality))
}.build()
)
val videoCapture = VideoCapture.withOutput(videoOutput)
@@ -178,7 +177,7 @@
// Cuttlefish API 29 has inconsistent resolution issue. See b/184015059.
assumeFalse(Build.MODEL.contains("Cuttlefish") && Build.VERSION.SDK_INT == 29)
- val targetResolution = QualitySelector.getResolution(cameraInfo, QUALITY_LOWEST)
+ val targetResolution = QualitySelector.getResolution(cameraInfo, Quality.LOWEST)
arrayOf(
Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_180, Surface.ROTATION_270
@@ -186,7 +185,7 @@
// Arrange.
val videoOutput = TestVideoOutput(
mediaSpec = MediaSpec.builder().configureVideo {
- it.setQualitySelector(QualitySelector.of(QUALITY_LOWEST))
+ it.setQualitySelector(QualitySelector.from(Quality.LOWEST))
}.build()
)
val videoCapture = VideoCapture.withOutput(videoOutput)
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
index 92c780f..3eec001 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/VideoRecordingTest.kt
@@ -205,7 +205,7 @@
}
val recorder = Recorder.Builder()
- .setQualitySelector(QualitySelector.of(quality)).build()
+ .setQualitySelector(QualitySelector.from(quality)).build()
val videoCapture = VideoCapture.withOutput(recorder)
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/AudioSourceTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/AudioSourceTest.kt
index cdb46b4..a870135 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/AudioSourceTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/AudioSourceTest.kt
@@ -68,14 +68,16 @@
}
fakeBufferProvider.setActive(true)
- audioSource = AudioSource.Builder()
- .setExecutor(CameraXExecutors.ioExecutor())
- .setAudioSource(AUDIO_SOURCE)
- .setSampleRate(SAMPLE_RATE)
- .setChannelCount(CHANNEL_COUNT)
- .setAudioFormat(AUDIO_FORMAT)
- .setBufferProvider(fakeBufferProvider)
- .build()
+ audioSource = AudioSource(
+ AudioSource.Settings.builder()
+ .setAudioSource(AUDIO_SOURCE)
+ .setSampleRate(SAMPLE_RATE)
+ .setChannelCount(CHANNEL_COUNT)
+ .setAudioFormat(AUDIO_FORMAT)
+ .build(),
+ CameraXExecutors.ioExecutor()
+ )
+ audioSource.setBufferProvider(fakeBufferProvider)
}
@After
@@ -123,4 +125,44 @@
verify(bufferFactoryInvocations, noInvocation(3000L, 6000L)).call()
}
}
+
+ @Test
+ fun canResetBufferProvider_beforeStarting() {
+ // Arrange
+ val localBufferFactoryInvocations = mock(Callable::class.java)
+ val localFakeBufferProvider = FakeBufferProvider {
+ localBufferFactoryInvocations.call()
+ FakeInputBuffer()
+ }
+
+ // Act
+ audioSource.setBufferProvider(localFakeBufferProvider)
+ audioSource.start()
+ localFakeBufferProvider.setActive(true)
+
+ // Assert.
+ // It should continuously send audio data by invoking BufferProvider#acquireBuffer
+ verify(localBufferFactoryInvocations, timeout(10000L).atLeast(3)).call()
+ }
+
+ @Test
+ fun canResetBufferProvider_afterStarting() {
+ // Arrange
+ val localBufferFactoryInvocations = mock(Callable::class.java)
+ val localFakeBufferProvider = FakeBufferProvider {
+ localBufferFactoryInvocations.call()
+ FakeInputBuffer()
+ }
+ audioSource.start()
+ fakeBufferProvider.setActive(true)
+ verify(bufferFactoryInvocations, timeout(10000L).atLeast(3)).call()
+
+ // Act
+ audioSource.setBufferProvider(localFakeBufferProvider)
+ localFakeBufferProvider.setActive(true)
+
+ // Assert.
+ // It should continuously send audio data by invoking BufferProvider#acquireBuffer
+ verify(localBufferFactoryInvocations, timeout(10000L).atLeast(3)).call()
+ }
}
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioEncoderConfigCamcorderProfileResolverTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioEncoderConfigCamcorderProfileResolverTest.kt
new file mode 100644
index 0000000..36147c4
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioEncoderConfigCamcorderProfileResolverTest.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config
+
+import android.content.Context
+import android.util.Range
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.CameraXUtil
+import androidx.camera.video.AudioSpec
+import androidx.camera.video.Quality
+import androidx.camera.video.VideoCapabilities
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 21)
+class AudioEncoderConfigCamcorderProfileResolverTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+ private val defaultAudioSpec = AudioSpec.builder().build()
+
+ private lateinit var cameraUseCaseAdapter: CameraUseCaseAdapter
+ private lateinit var videoCapabilities: VideoCapabilities
+
+ @Before
+ fun setUp() {
+ Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
+
+ CameraXUtil.initialize(
+ context,
+ Camera2Config.defaultConfig()
+ ).get()
+
+ val cameraInfo = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector).cameraInfo
+ videoCapabilities = VideoCapabilities.from(cameraInfo)
+ }
+
+ @After
+ fun tearDown() {
+ if (this::cameraUseCaseAdapter.isInitialized) {
+ runBlocking(Dispatchers.Main) {
+ cameraUseCaseAdapter.removeUseCases(cameraUseCaseAdapter.useCases)
+ }
+ }
+
+ CameraXUtil.shutdown().get(10, TimeUnit.SECONDS)
+ }
+
+ @Test
+ fun defaultAudioSpecAndAudioSourceProducesValidSettings() {
+ val supportedProfiles = videoCapabilities.supportedQualities.map {
+ videoCapabilities.getProfile(it)!!
+ }
+
+ supportedProfiles.forEach {
+ val sourceSettings =
+ AudioSourceSettingsCamcorderProfileResolver(defaultAudioSpec, it).get()
+ val config = AudioEncoderConfigCamcorderProfileResolver(
+ it.audioCodecMimeType!!,
+ it.requiredAudioProfile,
+ defaultAudioSpec,
+ sourceSettings,
+ it
+ ).get()
+
+ assertThat(config.mimeType).isEqualTo(it.audioCodecMimeType)
+ assertThat(config.bitrate).isEqualTo(it.audioBitRate)
+ assertThat(config.sampleRate).isEqualTo(it.audioSampleRate)
+ assertThat(config.channelCount).isEqualTo(it.audioChannels)
+ }
+ }
+
+ @Test
+ fun increasedChannelCountIncreasesBitrate() {
+ // Get default channel count
+ val profile = videoCapabilities.getProfile(Quality.HIGHEST)!!
+ val defaultSourceSettings =
+ AudioSourceSettingsCamcorderProfileResolver(defaultAudioSpec, profile).get()
+ val defaultConfig =
+ AudioEncoderConfigCamcorderProfileResolver(
+ profile.audioCodecMimeType!!,
+ profile.requiredAudioProfile,
+ defaultAudioSpec,
+ defaultSourceSettings,
+ profile
+ ).get()
+ val defaultChannelCount = defaultConfig.channelCount
+
+ val higherChannelCountSourceSettings =
+ defaultSourceSettings.toBuilder().setChannelCount(defaultChannelCount * 2).build()
+
+ val higherChannelCountConfig = AudioEncoderConfigCamcorderProfileResolver(
+ profile.audioCodecMimeType!!,
+ profile.requiredAudioProfile,
+ defaultAudioSpec,
+ higherChannelCountSourceSettings,
+ profile
+ ).get()
+
+ assertThat(higherChannelCountConfig.bitrate).isGreaterThan(defaultConfig.bitrate)
+ }
+
+ @Test
+ fun increasedSampleRateIncreasesBitrate() {
+ // Get default sample rate
+ val profile = videoCapabilities.getProfile(Quality.HIGHEST)!!
+ val defaultSourceSettings =
+ AudioSourceSettingsCamcorderProfileResolver(defaultAudioSpec, profile).get()
+ val defaultConfig =
+ AudioEncoderConfigCamcorderProfileResolver(
+ profile.audioCodecMimeType!!,
+ profile.requiredAudioProfile,
+ defaultAudioSpec,
+ defaultSourceSettings,
+ profile
+ ).get()
+ val defaultSampleRate = defaultConfig.sampleRate
+
+ val higherSampleRateSourceSettings =
+ defaultSourceSettings.toBuilder().setChannelCount(defaultSampleRate * 2).build()
+
+ val higherSampleRateConfig = AudioEncoderConfigCamcorderProfileResolver(
+ profile.audioCodecMimeType!!,
+ profile.requiredAudioProfile,
+ defaultAudioSpec,
+ higherSampleRateSourceSettings,
+ profile
+ ).get()
+
+ assertThat(higherSampleRateConfig.bitrate).isGreaterThan(defaultConfig.bitrate)
+ }
+
+ @Test
+ fun bitrateRangeInVideoSpecClampsBitrate() {
+ val profile = videoCapabilities.getProfile(Quality.HIGHEST)!!
+ val defaultSourceSettings =
+ AudioSourceSettingsCamcorderProfileResolver(defaultAudioSpec, profile).get()
+
+ val defaultBitrate = profile.audioBitRate
+
+ // Create audio spec with limit 20% higher than default.
+ val higherBitrate = (defaultBitrate * 1.2).toInt()
+ val higherAudioSpec =
+ AudioSpec.builder().setBitrate(Range(higherBitrate, Int.MAX_VALUE)).build()
+
+ // Create audio spec with limit 20% lower than default.
+ val lowerBitrate = (defaultBitrate * 0.8).toInt()
+ val lowerAudioSpec = AudioSpec.builder().setBitrate(Range(0, lowerBitrate)).build()
+
+ assertThat(
+ AudioEncoderConfigCamcorderProfileResolver(
+ profile.audioCodecMimeType!!,
+ profile.requiredAudioProfile,
+ higherAudioSpec,
+ defaultSourceSettings,
+ profile
+ ).get().bitrate
+ ).isEqualTo(higherBitrate)
+
+ assertThat(
+ AudioEncoderConfigCamcorderProfileResolver(
+ profile.audioCodecMimeType!!,
+ profile.requiredAudioProfile,
+ lowerAudioSpec,
+ defaultSourceSettings,
+ profile
+ ).get().bitrate
+ ).isEqualTo(lowerBitrate)
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioEncoderConfigDefaultResolverTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioEncoderConfigDefaultResolverTest.kt
new file mode 100644
index 0000000..4545f43
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioEncoderConfigDefaultResolverTest.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config
+
+import android.media.MediaCodecInfo
+import android.util.Range
+import androidx.camera.video.AudioSpec
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 21)
+class AudioEncoderConfigDefaultResolverTest {
+
+ companion object {
+ const val MIME_TYPE = "audio/mp4a-latm"
+ const val ENCODER_PROFILE = MediaCodecInfo.CodecProfileLevel.AACObjectLC
+ }
+
+ private val defaultAudioSpec = AudioSpec.builder().build()
+ private val defaultAudioSourceSettings =
+ AudioSourceSettingsDefaultResolver(defaultAudioSpec).get()
+
+ @Test
+ fun defaultAudioSpecProducesValidSettings() {
+ val resolvedAudioConfig = AudioEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ ENCODER_PROFILE,
+ defaultAudioSpec,
+ defaultAudioSourceSettings
+ ).get()
+
+ assertThat(resolvedAudioConfig.mimeType).isEqualTo(MIME_TYPE)
+ assertThat(resolvedAudioConfig.profile).isEqualTo(ENCODER_PROFILE)
+ assertThat(resolvedAudioConfig.channelCount)
+ .isEqualTo(defaultAudioSourceSettings.channelCount)
+ assertThat(resolvedAudioConfig.sampleRate).isEqualTo(defaultAudioSourceSettings.sampleRate)
+ assertThat(resolvedAudioConfig.bitrate).isGreaterThan(0)
+ }
+
+ @Test
+ fun increasedChannelCountIncreasesBitrate() {
+ // Get default channel count
+ val defaultConfig =
+ AudioEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ ENCODER_PROFILE,
+ defaultAudioSpec,
+ defaultAudioSourceSettings
+ ).get()
+ val defaultChannelCount = defaultConfig.channelCount
+
+ val higherChannelCountSourceSettings =
+ defaultAudioSourceSettings.toBuilder().setChannelCount(defaultChannelCount * 2).build()
+
+ val higherChannelCountConfig = AudioEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ ENCODER_PROFILE,
+ defaultAudioSpec,
+ higherChannelCountSourceSettings
+ ).get()
+
+ assertThat(higherChannelCountConfig.bitrate).isGreaterThan(defaultConfig.bitrate)
+ }
+
+ @Test
+ fun increasedSampleRateIncreasesBitrate() {
+ // Get default sample rate
+ val defaultConfig =
+ AudioEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ ENCODER_PROFILE,
+ defaultAudioSpec,
+ defaultAudioSourceSettings
+ ).get()
+ val defaultSampleRate = defaultConfig.sampleRate
+
+ val higherSampleRateSourceSettings =
+ defaultAudioSourceSettings.toBuilder().setSampleRate(defaultSampleRate * 2).build()
+
+ val higherSampleRateConfig = AudioEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ ENCODER_PROFILE,
+ defaultAudioSpec,
+ higherSampleRateSourceSettings
+ ).get()
+
+ assertThat(higherSampleRateConfig.bitrate).isGreaterThan(defaultConfig.bitrate)
+ }
+
+ @Test
+ fun bitrateRangeInVideoSpecClampsBitrate() {
+ val defaultConfig =
+ AudioEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ ENCODER_PROFILE,
+ defaultAudioSpec,
+ defaultAudioSourceSettings
+ ).get()
+ val defaultBitrate = defaultConfig.bitrate
+
+ // Create audio spec with limit 20% higher than default.
+ val higherBitrate = (defaultBitrate * 1.2).toInt()
+ val higherAudioSpec =
+ AudioSpec.builder().setBitrate(Range(higherBitrate, Int.MAX_VALUE)).build()
+
+ // Create audio spec with limit 20% lower than default.
+ val lowerBitrate = (defaultBitrate * 0.8).toInt()
+ val lowerAudioSpec = AudioSpec.builder().setBitrate(Range(0, lowerBitrate)).build()
+
+ assertThat(
+ AudioEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ ENCODER_PROFILE,
+ higherAudioSpec,
+ defaultAudioSourceSettings
+ ).get().bitrate
+ ).isEqualTo(higherBitrate)
+
+ assertThat(
+ AudioEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ ENCODER_PROFILE,
+ lowerAudioSpec,
+ defaultAudioSourceSettings
+ ).get().bitrate
+ ).isEqualTo(lowerBitrate)
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSourceSettingsCamcorderProfileResolverTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSourceSettingsCamcorderProfileResolverTest.kt
new file mode 100644
index 0000000..bb5986b
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSourceSettingsCamcorderProfileResolverTest.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config
+
+import android.content.Context
+import android.media.AudioFormat
+import android.media.MediaRecorder
+import android.util.Range
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.testing.AudioUtil
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.CameraXUtil
+import androidx.camera.video.AudioSpec
+import androidx.camera.video.Quality
+import androidx.camera.video.VideoCapabilities
+import androidx.camera.video.internal.AudioSource
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 21)
+class AudioSourceSettingsCamcorderProfileResolverTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+ private val defaultAudioSpec = AudioSpec.builder().build()
+
+ private lateinit var cameraUseCaseAdapter: CameraUseCaseAdapter
+ private lateinit var videoCapabilities: VideoCapabilities
+
+ @Before
+ fun setUp() {
+ Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
+ // Skip for b/168175357
+ Assume.assumeTrue(AudioUtil.canStartAudioRecord(MediaRecorder.AudioSource.CAMCORDER))
+
+ CameraXUtil.initialize(
+ context,
+ Camera2Config.defaultConfig()
+ ).get()
+
+ val cameraInfo = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector).cameraInfo
+ videoCapabilities = VideoCapabilities.from(cameraInfo)
+ }
+
+ @After
+ fun tearDown() {
+ if (this::cameraUseCaseAdapter.isInitialized) {
+ runBlocking(Dispatchers.Main) {
+ cameraUseCaseAdapter.removeUseCases(cameraUseCaseAdapter.useCases)
+ }
+ }
+
+ CameraXUtil.shutdown().get(10, TimeUnit.SECONDS)
+ }
+
+ @Test
+ fun defaultAudioSpecResolvesToSupportedSettings() {
+ val resolvedSettings = videoCapabilities.supportedQualities.map {
+ val camcorderProfile = videoCapabilities.getProfile(it)
+ AudioSourceSettingsCamcorderProfileResolver(defaultAudioSpec, camcorderProfile!!).get()
+ }
+
+ resolvedSettings.forEach {
+ assertThat(
+ AudioSource.isSettingsSupported(
+ it.sampleRate,
+ it.channelCount,
+ it.audioFormat
+ )
+ )
+ }
+ }
+
+ @Test
+ fun nonDefaultAudioSpecResolvesToSupportedSampleRate() {
+ val audioSpecs = listOf(
+ AudioSpec.builder().setSampleRate(Range(0, 1000)).build(),
+ AudioSpec.builder().setSampleRate(Range(1000, 10000)).build(),
+ AudioSpec.builder().setSampleRate(Range(10000, 100000)).build()
+ )
+
+ val resolvedSettings = videoCapabilities.supportedQualities.flatMap { quality ->
+ val camcorderProfile = videoCapabilities.getProfile(quality)
+ audioSpecs.map {
+ AudioSourceSettingsCamcorderProfileResolver(it, camcorderProfile!!).get()
+ }
+ }
+
+ resolvedSettings.forEach {
+ assertThat(
+ AudioSource.isSettingsSupported(
+ it.sampleRate,
+ it.channelCount,
+ it.audioFormat
+ )
+ )
+ }
+ }
+
+ @Test
+ fun sampleRateCanOverrideCamcorderProfile_ifSupported() {
+ val profile = videoCapabilities.getProfile(Quality.HIGHEST)
+ // Get a config using the default audio spec to retrieve the source format
+ // Note: This relies on resolution of sample rate and source format being independent.
+ // If a dependency between the two is introduced, this will stop working and will
+ // need to be rewritten.
+ val autoCamcorderProfileConfig =
+ AudioSourceSettingsCamcorderProfileResolver(defaultAudioSpec, profile!!).get()
+ // Try to find a sample rate that is supported, but not the
+ // sample rate advertised by CamcorderProfile
+ val nonReportedSampleRate = AudioSource.COMMON_SAMPLE_RATES.firstOrNull {
+ it != profile.audioSampleRate && AudioSource.isSettingsSupported(
+ it,
+ profile.audioChannels,
+ autoCamcorderProfileConfig.audioFormat
+ )
+ }
+ Assume.assumeTrue(
+ "Device does not support any other common sample rates. Cannot override.",
+ nonReportedSampleRate != null
+ )
+
+ // Create an audio spec that overrides the auto sample rate behavior
+ val audioSpec =
+ AudioSpec.builder().setSampleRate(Range(nonReportedSampleRate!!, nonReportedSampleRate))
+ .build()
+ val resolvedSampleRate =
+ AudioSourceSettingsCamcorderProfileResolver(audioSpec, profile).get().sampleRate
+
+ assertThat(resolvedSampleRate).isNotEqualTo(profile.audioSampleRate)
+ assertThat(resolvedSampleRate).isEqualTo(nonReportedSampleRate)
+ }
+
+ @Test
+ fun audioSpecDefaultProducesValidSourceEnum() {
+ val profile = videoCapabilities.getProfile(Quality.HIGHEST)
+ val audioSpec = AudioSpec.builder().build()
+ val resolvedAudioSourceEnum =
+ AudioSourceSettingsCamcorderProfileResolver(
+ audioSpec,
+ profile!!
+ ).get().audioSource
+
+ assertThat(resolvedAudioSourceEnum).isAnyOf(
+ MediaRecorder.AudioSource.CAMCORDER,
+ MediaRecorder.AudioSource.MIC
+ )
+ }
+
+ @Test
+ fun audioSpecDefaultProducesValidSourceFormat() {
+ val profile = videoCapabilities.getProfile(Quality.HIGHEST)
+ val audioSpec = AudioSpec.builder().build()
+ val resolvedAudioSourceFormat =
+ AudioSourceSettingsCamcorderProfileResolver(
+ audioSpec,
+ profile!!
+ ).get().audioFormat
+
+ assertThat(resolvedAudioSourceFormat).isNotEqualTo(AudioFormat.ENCODING_INVALID)
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSourceSettingsDefaultResolverTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSourceSettingsDefaultResolverTest.kt
new file mode 100644
index 0000000..44e96cc
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/AudioSourceSettingsDefaultResolverTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config
+
+import android.media.AudioFormat
+import android.media.MediaRecorder
+import android.util.Range
+import androidx.camera.video.AudioSpec
+import androidx.camera.video.internal.AudioSource
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 21)
+class AudioSourceSettingsDefaultResolverTest {
+
+ @Test
+ fun sampleRateRangeResolvesToSupportedSampleRate() {
+ val audioSpecs = listOf(
+ AudioSpec.builder().build(),
+ AudioSpec.builder().setSampleRate(Range(0, 1000)).build(),
+ AudioSpec.builder().setSampleRate(Range(1000, 10000)).build(),
+ AudioSpec.builder().setSampleRate(Range(10000, 100000)).build()
+ )
+
+ audioSpecs.forEach {
+ val audioSettings = AudioSourceSettingsDefaultResolver(it).get()
+ assertThat(
+ AudioSource.isSettingsSupported(
+ audioSettings.sampleRate,
+ audioSettings.channelCount,
+ audioSettings.audioFormat
+ )
+ )
+ }
+ }
+
+ @Test
+ fun audioSpecDefaultProducesValidSourceEnum() {
+ val audioSpec = AudioSpec.builder().build()
+ val resolvedAudioSourceEnum =
+ AudioSourceSettingsDefaultResolver(audioSpec).get().audioSource
+
+ assertThat(resolvedAudioSourceEnum).isAnyOf(
+ MediaRecorder.AudioSource.CAMCORDER,
+ MediaRecorder.AudioSource.MIC
+ )
+ }
+
+ @Test
+ fun audioSpecDefaultProducesValidSourceFormat() {
+ val audioSpec = AudioSpec.builder().build()
+ val resolvedAudioSourceFormat =
+ AudioSourceSettingsDefaultResolver(audioSpec).get().audioFormat
+
+ assertThat(resolvedAudioSourceFormat).isNotEqualTo(AudioFormat.ENCODING_INVALID)
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigCamcorderProfileResolverTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigCamcorderProfileResolverTest.kt
new file mode 100644
index 0000000..3a71162
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigCamcorderProfileResolverTest.kt
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config
+
+import android.content.Context
+import android.util.Range
+import android.util.Size
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.internal.CameraUseCaseAdapter
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.CameraXUtil
+import androidx.camera.video.Quality
+import androidx.camera.video.VideoCapabilities
+import androidx.camera.video.VideoSpec
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 21)
+class VideoEncoderConfigCamcorderProfileResolverTest {
+
+ companion object {
+ // TODO(b/177918193): We currently cannot communicate the frame rate to the camera,
+ // so we only support 30fps. For now we want to ensure we only ever get an FPS of 30,
+ // but this check can be removed once we can change frame rate.
+ const val FIXED_FRAME_RATE = 30
+ }
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+ private val defaultVideoSpec = VideoSpec.builder().build()
+
+ private lateinit var cameraUseCaseAdapter: CameraUseCaseAdapter
+ private lateinit var videoCapabilities: VideoCapabilities
+
+ @Before
+ fun setUp() {
+ Assume.assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
+
+ CameraXUtil.initialize(
+ context,
+ Camera2Config.defaultConfig()
+ ).get()
+
+ val cameraInfo = CameraUtil.createCameraUseCaseAdapter(context, cameraSelector).cameraInfo
+ videoCapabilities = VideoCapabilities.from(cameraInfo)
+ }
+
+ @After
+ fun tearDown() {
+ if (this::cameraUseCaseAdapter.isInitialized) {
+ runBlocking(Dispatchers.Main) {
+ cameraUseCaseAdapter.removeUseCases(cameraUseCaseAdapter.useCases)
+ }
+ }
+
+ CameraXUtil.shutdown().get(10, TimeUnit.SECONDS)
+ }
+
+ @Test
+ fun defaultVideoSpecProducesValidSettings_forSurfaceSizeEquivalentToQuality() {
+ val supportedProfiles = videoCapabilities.supportedQualities.map {
+ videoCapabilities.getProfile(it)!!
+ }
+
+ supportedProfiles.forEach {
+ val config = VideoEncoderConfigCamcorderProfileResolver(
+ it.videoCodecMimeType!!,
+ defaultVideoSpec,
+ Size(it.videoFrameWidth, it.videoFrameHeight),
+ it
+ ).get()
+
+ assertThat(config.mimeType).isEqualTo(it.videoCodecMimeType)
+ // TODO(b/177918193): Frame rate ratio needs to scale bitrate since we use a fixed
+ // frame rate. The output config frame rate may not match the profile frame rate.
+ val scaledBitrate =
+ (it.videoBitRate * config.frameRate.toDouble() / it.videoFrameRate).toInt()
+ assertThat(config.bitrate).isEqualTo(scaledBitrate)
+ assertThat(config.resolution).isEqualTo(Size(it.videoFrameWidth, it.videoFrameHeight))
+ assertThat(config.frameRate).isEqualTo(FIXED_FRAME_RATE)
+ }
+ }
+
+ @Test
+ fun bitrateIncreasesOrDecreasesWithIncreaseOrDecreaseInSurfaceSize() {
+ val profile = videoCapabilities.getProfile(Quality.HIGHEST)!!
+ val surfaceSize = Size(profile.videoFrameWidth, profile.videoFrameHeight)
+
+ val defaultBitrate = VideoEncoderConfigCamcorderProfileResolver(
+ profile.videoCodecMimeType!!,
+ defaultVideoSpec,
+ surfaceSize,
+ profile
+ ).get().bitrate
+
+ val increasedSurfaceSize = Size(surfaceSize.width + 100, surfaceSize.height + 100)
+ val decreasedSurfaceSize = Size(surfaceSize.width - 100, surfaceSize.height - 100)
+
+ assertThat(
+ VideoEncoderConfigCamcorderProfileResolver(
+ profile.videoCodecMimeType!!,
+ defaultVideoSpec,
+ increasedSurfaceSize,
+ profile
+ ).get().bitrate
+ ).isGreaterThan(defaultBitrate)
+
+ assertThat(
+ VideoEncoderConfigCamcorderProfileResolver(
+ profile.videoCodecMimeType!!,
+ defaultVideoSpec,
+ decreasedSurfaceSize,
+ profile
+ ).get().bitrate
+ ).isLessThan(defaultBitrate)
+ }
+
+ @Test
+ fun bitrateRangeInVideoSpecClampsBitrate() {
+ val profile = videoCapabilities.getProfile(Quality.HIGHEST)!!
+ val surfaceSize = Size(profile.videoFrameWidth, profile.videoFrameHeight)
+
+ val defaultBitrate = VideoEncoderConfigCamcorderProfileResolver(
+ profile.videoCodecMimeType!!,
+ defaultVideoSpec,
+ surfaceSize,
+ profile
+ ).get().bitrate
+
+ // Create video spec with limit 20% higher than default.
+ val higherBitrate = (defaultBitrate * 1.2).toInt()
+ val higherVideoSpec =
+ VideoSpec.builder().setBitrate(Range(higherBitrate, Int.MAX_VALUE)).build()
+
+ // Create video spec with limit 20% lower than default.
+ val lowerBitrate = (defaultBitrate * 0.8).toInt()
+ val lowerVideoSpec = VideoSpec.builder().setBitrate(Range(0, lowerBitrate)).build()
+
+ assertThat(
+ VideoEncoderConfigCamcorderProfileResolver(
+ profile.videoCodecMimeType!!,
+ higherVideoSpec,
+ surfaceSize,
+ profile
+ ).get().bitrate
+ ).isEqualTo(higherBitrate)
+
+ assertThat(
+ VideoEncoderConfigCamcorderProfileResolver(
+ profile.videoCodecMimeType!!,
+ lowerVideoSpec,
+ surfaceSize,
+ profile
+ ).get().bitrate
+ ).isEqualTo(lowerBitrate)
+ }
+
+ // TODO(b/177918193): We currently cannot communicate the frame rate to the camera,
+ // so we only support 30fps. Ensure the encoder config always is 30 so the encoder
+ // gets the correct frame rate.
+ // This test can be removed once setting the frame rate is supported.
+ @Test
+ fun frameRateIsAlways30() {
+ // Give a VideoSpec with a frame rate higher than 30
+ val videoSpec = VideoSpec.builder().setFrameRate(Range(60, 60)).build()
+ val profile = videoCapabilities.getProfile(Quality.HIGHEST)!!
+ val surfaceSize = Size(profile.videoFrameWidth, profile.videoFrameHeight)
+
+ assertThat(
+ VideoEncoderConfigCamcorderProfileResolver(
+ profile.videoCodecMimeType!!,
+ videoSpec,
+ surfaceSize,
+ profile
+ ).get().frameRate
+ ).isEqualTo(
+ FIXED_FRAME_RATE
+ )
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolverTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolverTest.kt
new file mode 100644
index 0000000..68e0614
--- /dev/null
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolverTest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config
+
+import android.util.Range
+import androidx.camera.testing.CamcorderProfileUtil
+import androidx.camera.video.VideoSpec
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@SdkSuppress(minSdkVersion = 21)
+class VideoEncoderConfigDefaultResolverTest {
+
+ companion object {
+ const val MIME_TYPE = "video/avc"
+
+ // TODO(b/177918193): We currently cannot communicate the frame rate to the camera,
+ // so we only support 30fps. For now we want to ensure we only ever get an FPS of 30,
+ // but this check can be removed once we can change frame rate.
+ const val FIXED_FRAME_RATE = 30
+ }
+
+ private val defaultVideoSpec = VideoSpec.builder().build()
+
+ @Test
+ fun defaultVideoSpecProducesValidSettings_forDifferentSurfaceSizes() {
+ val surfaceSizeCif = CamcorderProfileUtil.RESOLUTION_CIF
+ val surfaceSize720p = CamcorderProfileUtil.RESOLUTION_720P
+ val surfaceSize1080p = CamcorderProfileUtil.RESOLUTION_1080P
+
+ val configSupplierCif =
+ VideoEncoderConfigDefaultResolver(MIME_TYPE, defaultVideoSpec, surfaceSizeCif)
+ val configSupplier720p =
+ VideoEncoderConfigDefaultResolver(MIME_TYPE, defaultVideoSpec, surfaceSize720p)
+ val configSupplier1080p =
+ VideoEncoderConfigDefaultResolver(MIME_TYPE, defaultVideoSpec, surfaceSize1080p)
+
+ val configCif = configSupplierCif.get()
+ assertThat(configCif.mimeType).isEqualTo(MIME_TYPE)
+ assertThat(configCif.bitrate).isGreaterThan(0)
+ assertThat(configCif.resolution).isEqualTo(surfaceSizeCif)
+ assertThat(configCif.frameRate).isEqualTo(FIXED_FRAME_RATE)
+
+ val config720p = configSupplier720p.get()
+ assertThat(config720p.mimeType).isEqualTo(MIME_TYPE)
+ assertThat(config720p.bitrate).isGreaterThan(0)
+ assertThat(config720p.resolution).isEqualTo(surfaceSize720p)
+ assertThat(config720p.frameRate).isEqualTo(FIXED_FRAME_RATE)
+
+ val config1080p = configSupplier1080p.get()
+ assertThat(config1080p.mimeType).isEqualTo(MIME_TYPE)
+ assertThat(config1080p.bitrate).isGreaterThan(0)
+ assertThat(config1080p.resolution).isEqualTo(surfaceSize1080p)
+ assertThat(config1080p.frameRate).isEqualTo(FIXED_FRAME_RATE)
+ }
+
+ @Test
+ fun bitrateRangeInVideoSpecClampsBitrate() {
+ val surfaceSize720p = CamcorderProfileUtil.RESOLUTION_720P
+
+ // Get default bit rate for this size
+ val defaultConfig =
+ VideoEncoderConfigDefaultResolver(MIME_TYPE, defaultVideoSpec, surfaceSize720p).get()
+ val defaultBitrate = defaultConfig.bitrate
+
+ // Create video spec with limit 20% higher than default.
+ val higherBitrate = (defaultBitrate * 1.2).toInt()
+ val higherVideoSpec =
+ VideoSpec.builder().setBitrate(Range(higherBitrate, Int.MAX_VALUE)).build()
+
+ // Create video spec with limit 20% lower than default.
+ val lowerBitrate = (defaultBitrate * 0.8).toInt()
+ val lowerVideoSpec = VideoSpec.builder().setBitrate(Range(0, lowerBitrate)).build()
+
+ assertThat(
+ VideoEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ higherVideoSpec,
+ surfaceSize720p
+ ).get().bitrate
+ ).isEqualTo(higherBitrate)
+
+ assertThat(
+ VideoEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ lowerVideoSpec,
+ surfaceSize720p
+ ).get().bitrate
+ ).isEqualTo(lowerBitrate)
+ }
+
+ // TODO(b/177918193): We currently cannot communicate the frame rate to the camera,
+ // so we only support 30fps. Ensure the encoder config always is 30 so the encoder
+ // gets the correct frame rate.
+ // This test can be removed once setting the frame rate is supported.
+ @Test
+ fun frameRateIsAlways30() {
+ // Give a VideoSpec with a frame rate higher than 30
+ val videoSpec = VideoSpec.builder().setBitrate(Range(60, 60)).build()
+ val size = CamcorderProfileUtil.RESOLUTION_1080P
+
+ assertThat(
+ VideoEncoderConfigDefaultResolver(
+ MIME_TYPE,
+ videoSpec,
+ size
+ ).get().frameRate
+ ).isEqualTo(
+ FIXED_FRAME_RATE
+ )
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/AudioEncoderTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/AudioEncoderTest.kt
index b9dfe47..e96a2fd 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/AudioEncoderTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/AudioEncoderTest.kt
@@ -15,6 +15,7 @@
*/
package androidx.camera.video.internal.encoder
+import android.media.MediaCodecInfo
import androidx.camera.core.impl.Observable.Observer
import androidx.camera.core.impl.utils.executor.CameraXExecutors
import androidx.camera.video.internal.BufferProvider
@@ -58,6 +59,7 @@
companion object {
private const val MIME_TYPE = "audio/mp4a-latm"
+ private const val ENCODER_PROFILE = MediaCodecInfo.CodecProfileLevel.AACObjectLC
private const val BIT_RATE = 64000
private const val SAMPLE_RATE = 44100
private const val CHANNEL_COUNT = 1
@@ -80,6 +82,7 @@
CameraXExecutors.ioExecutor(),
AudioEncoderConfig.builder()
.setMimeType(MIME_TYPE)
+ .setProfile(ENCODER_PROFILE)
.setBitrate(BIT_RATE)
.setSampleRate(SAMPLE_RATE)
.setChannelCount(CHANNEL_COUNT)
diff --git a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/VideoEncoderTest.kt b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/VideoEncoderTest.kt
index 696f9ea..7a7197c 100644
--- a/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/VideoEncoderTest.kt
+++ b/camera/camera-video/src/androidTest/java/androidx/camera/video/internal/encoder/VideoEncoderTest.kt
@@ -38,6 +38,7 @@
import androidx.camera.testing.CameraXUtil
import androidx.camera.testing.SurfaceTextureProvider
import androidx.camera.testing.SurfaceTextureProvider.SurfaceTextureCallback
+import androidx.camera.video.Quality
import androidx.camera.video.QualitySelector
import androidx.camera.video.internal.compat.quirk.DeactivateEncoderSurfaceBeforeStopEncoderQuirk
import androidx.camera.video.internal.compat.quirk.DeviceQuirks
@@ -319,7 +320,7 @@
private fun initVideoEncoder() {
val cameraInfo = camera.cameraInfo as CameraInfoInternal
- val resolution = QualitySelector.getResolution(cameraInfo, QualitySelector.QUALITY_LOWEST)
+ val resolution = QualitySelector.getResolution(cameraInfo, Quality.LOWEST)
assumeTrue(resolution != null)
videoEncoderConfig = VideoEncoderConfig.builder()
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/AudioSpec.java b/camera/camera-video/src/main/java/androidx/camera/video/AudioSpec.java
index c08e2d1..560a601 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/AudioSpec.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/AudioSpec.java
@@ -43,11 +43,11 @@
/**
* The audio source format representing no preference for audio source format.
*/
- static final int SOURCE_FORMAT_AUTO = -1;
+ public static final int SOURCE_FORMAT_AUTO = -1;
/**
* The PCM 16 bit per sample audio source format. Guaranteed to be supported by all devices.
*/
- static final int SOURCE_FORMAT_PCM_16BIT = AudioFormat.ENCODING_PCM_16BIT;
+ public static final int SOURCE_FORMAT_PCM_16BIT = AudioFormat.ENCODING_PCM_16BIT;
@IntDef({SOURCE_FORMAT_AUTO, SOURCE_FORMAT_PCM_16BIT})
@Retention(RetentionPolicy.SOURCE)
@@ -141,7 +141,7 @@
/** Gets the audio format. */
@SourceFormat
- abstract int getSourceFormat();
+ public abstract int getSourceFormat();
/** Gets the audio source. */
@Source
@@ -195,7 +195,7 @@
* <p>If not set, defaults to {@link #SOURCE_FORMAT_AUTO}.
*/
@NonNull
- abstract Builder setSourceFormat(@SourceFormat int audioFormat);
+ public abstract Builder setSourceFormat(@SourceFormat int audioFormat);
/**
* Sets the audio source.
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/FallbackStrategy.java b/camera/camera-video/src/main/java/androidx/camera/video/FallbackStrategy.java
new file mode 100644
index 0000000..e45ba08
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/FallbackStrategy.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import com.google.auto.value.AutoValue;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A class represents the strategy that will be adopted when the device does not support all the
+ * desired {@link Quality} in {@link QualitySelector} in order to select the quality as possible.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class FallbackStrategy {
+
+ // Restrict access to sealed class
+ private FallbackStrategy() {
+ }
+
+ /**
+ * Returns a fallback strategy that will choose the quality that is closest to and higher
+ * than the input quality.
+ *
+ * <p>If that can not result in a supported quality, choose the quality that is closest to
+ * and lower than the input quality.
+ */
+ @NonNull
+ public static FallbackStrategy higherQualityOrLowerThan(@NonNull Quality quality) {
+ return new AutoValue_FallbackStrategy_RuleStrategy(quality,
+ FALLBACK_RULE_HIGHER_OR_LOWER);
+ }
+
+ /**
+ * Returns a fallback strategy that will choose the quality that is closest to and higher
+ * than the input quality.
+ */
+ @NonNull
+ public static FallbackStrategy higherQualityThan(@NonNull Quality quality) {
+ return new AutoValue_FallbackStrategy_RuleStrategy(quality,
+ FALLBACK_RULE_HIGHER);
+ }
+
+ /**
+ * Returns a fallback strategy that will choose the quality that is closest to and lower than
+ * the input quality.
+ *
+ * <p>If that can not result in a supported quality, choose the quality that is closest to
+ * and higher than the input quality.
+ */
+ @NonNull
+ public static FallbackStrategy lowerQualityOrHigherThan(@NonNull Quality quality) {
+ return new AutoValue_FallbackStrategy_RuleStrategy(quality,
+ FALLBACK_RULE_LOWER_OR_HIGHER);
+ }
+
+ /**
+ * Returns a fallback strategy that will choose the quality that is closest to and higher
+ * than the input quality.
+ */
+ @NonNull
+ public static FallbackStrategy lowerQualityThan(@NonNull Quality quality) {
+ return new AutoValue_FallbackStrategy_RuleStrategy(quality,
+ FALLBACK_RULE_LOWER);
+ }
+
+ static final int FALLBACK_RULE_NONE = 0;
+
+ /**
+ * Choose the quality that is closest to and higher than the desired quality. If that can not
+ * result in a supported quality, choose the quality that is closest to and lower than the
+ * desired quality.
+ */
+ static final int FALLBACK_RULE_HIGHER_OR_LOWER = 1;
+
+ /**
+ * Choose the quality that is closest to and higher than the desired quality.
+ */
+ static final int FALLBACK_RULE_HIGHER = 2;
+
+ /**
+ * Choose the quality that is closest to and lower than the desired quality. If that can not
+ * result in a supported quality, choose the quality that is closest to and higher than the
+ * desired quality.
+ */
+ static final int FALLBACK_RULE_LOWER_OR_HIGHER = 3;
+
+ /**
+ * Choose the quality that is closest to and lower than the desired quality.
+ */
+ static final int FALLBACK_RULE_LOWER = 4;
+
+ /**
+ * The fallback strategies when desired quality is not supported.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({FALLBACK_RULE_NONE,
+ FALLBACK_RULE_HIGHER_OR_LOWER,
+ FALLBACK_RULE_HIGHER,
+ FALLBACK_RULE_LOWER_OR_HIGHER,
+ FALLBACK_RULE_LOWER
+ })
+ @interface FallbackRule {
+ }
+
+ /**
+ * The strategy that no fallback strategy will be applied.
+ */
+ static final FallbackStrategy NONE =
+ new AutoValue_FallbackStrategy_RuleStrategy(Quality.NONE, FALLBACK_RULE_NONE);
+
+ @AutoValue
+ abstract static class RuleStrategy extends FallbackStrategy {
+ @NonNull
+ abstract Quality getFallbackQuality();
+
+ @FallbackRule
+ abstract int getFallbackRule();
+ }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/MediaSpec.java b/camera/camera-video/src/main/java/androidx/camera/video/MediaSpec.java
index 43228a4..aedcfc7 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/MediaSpec.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/MediaSpec.java
@@ -17,6 +17,7 @@
package androidx.camera.video;
import android.annotation.SuppressLint;
+import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
@@ -25,12 +26,14 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
+import androidx.camera.video.internal.encoder.EncoderConfig;
import androidx.core.util.Consumer;
import com.google.auto.value.AutoValue;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* MediaSpec communicates the encoding type and encoder-specific options for both the
@@ -42,10 +45,12 @@
@AutoValue
public abstract class MediaSpec {
- private static final String AUDIO_FORMAT_MPEG4 = MediaFormat.MIMETYPE_AUDIO_AAC;
- private static final String AUDIO_FORMAT_WEBM = MediaFormat.MIMETYPE_AUDIO_VORBIS;
- private static final String VIDEO_FORMAT_MPEG4 = MediaFormat.MIMETYPE_VIDEO_AVC;
- private static final String VIDEO_FORMAT_WEBM = MediaFormat.MIMETYPE_VIDEO_VP8;
+ private static final String AUDIO_ENCODER_MIME_MPEG4_DEFAULT = MediaFormat.MIMETYPE_AUDIO_AAC;
+ private static final String AUDIO_ENCODER_MIME_WEBM_DEFAULT = MediaFormat.MIMETYPE_AUDIO_VORBIS;
+ private static final String VIDEO_ENCODER_MIME_MPEG4_DEFAULT = MediaFormat.MIMETYPE_VIDEO_AVC;
+ private static final String VIDEO_ENCODER_MIME_WEBM_DEFAULT = MediaFormat.MIMETYPE_VIDEO_VP8;
+
+ private static final int AAC_DEFAULT_PROFILE = MediaCodecInfo.CodecProfileLevel.AACObjectLC;
/** The output format representing no preference for output format. */
public static final int OUTPUT_FORMAT_AUTO = -1;
@@ -65,27 +70,36 @@
static String outputFormatToAudioMime(@OutputFormat int outputFormat) {
switch (outputFormat) {
case MediaSpec.OUTPUT_FORMAT_WEBM:
- return AUDIO_FORMAT_WEBM;
+ return AUDIO_ENCODER_MIME_WEBM_DEFAULT;
case MediaSpec.OUTPUT_FORMAT_MPEG_4:
// Fall-through
case MediaSpec.OUTPUT_FORMAT_AUTO:
// Fall-through
default:
- return AUDIO_FORMAT_MPEG4;
+ return AUDIO_ENCODER_MIME_MPEG4_DEFAULT;
}
}
+ static int outputFormatToAudioProfile(@OutputFormat int outputFormat) {
+ String audioMime = outputFormatToAudioMime(outputFormat);
+ if (Objects.equals(audioMime, MediaFormat.MIMETYPE_AUDIO_AAC)) {
+ return AAC_DEFAULT_PROFILE;
+ }
+
+ return EncoderConfig.CODEC_PROFILE_NONE;
+ }
+
@NonNull
static String outputFormatToVideoMime(@OutputFormat int outputFormat) {
switch (outputFormat) {
case MediaSpec.OUTPUT_FORMAT_WEBM:
- return VIDEO_FORMAT_WEBM;
+ return VIDEO_ENCODER_MIME_WEBM_DEFAULT;
case MediaSpec.OUTPUT_FORMAT_MPEG_4:
// Fall-through
case MediaSpec.OUTPUT_FORMAT_AUTO:
// Fall-through
default:
- return VIDEO_FORMAT_MPEG4;
+ return VIDEO_ENCODER_MIME_MPEG4_DEFAULT;
}
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/Quality.java b/camera/camera-video/src/main/java/androidx/camera/video/Quality.java
new file mode 100644
index 0000000..2275709
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/Quality.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video;
+
+import android.media.CamcorderProfile;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A class representing video quality constraints that will be used by {@link QualitySelector} to
+ * choose video resolution and appropriate encoding parameters.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class Quality {
+
+ // Restrict access to sealed class
+ private Quality() {
+ }
+
+ /**
+ * Standard Definition (SD) 480p video quality.
+ *
+ * <p>This video quality usually corresponds to a resolution of 720 x 480 (480p) pixels.
+ */
+ public static final Quality SD = ConstantQuality.of(CamcorderProfile.QUALITY_480P, "SD");
+
+ /**
+ * High Definition (HD) video quality.
+ *
+ * <p>This video quality usually corresponds to a resolution of 1280 x 720 (720p) pixels.
+ */
+ public static final Quality HD = ConstantQuality.of(CamcorderProfile.QUALITY_720P, "HD");
+
+ /**
+ * Full High Definition (FHD) 1080p video quality.
+ *
+ * <p>This video quality usually corresponds to a resolution of 1920 x 1080 (1080p) pixels.
+ */
+ public static final Quality FHD = ConstantQuality.of(CamcorderProfile.QUALITY_1080P, "FHD");
+
+ /**
+ * Ultra High Definition (UHD) 2160p video quality.
+ *
+ * <p>This video quality usually corresponds to a resolution of 3840 x 2160 (2160p) pixels.
+ */
+ public static final Quality UHD = ConstantQuality.of(CamcorderProfile.QUALITY_2160P, "UHD");
+
+ /**
+ * The lowest video quality supported by the video frame producer.
+ */
+ public static final Quality LOWEST = ConstantQuality.of(CamcorderProfile.QUALITY_LOW, "LOWEST");
+
+ /**
+ * The highest video quality supported by the video frame producer.
+ */
+ public static final Quality HIGHEST = ConstantQuality.of(CamcorderProfile.QUALITY_HIGH,
+ "HIGHEST");
+
+ static final Quality NONE = ConstantQuality.of(-1, "NONE");
+
+ /** All quality constants. */
+ private static final Set<Quality> QUALITIES =
+ new HashSet<>(Arrays.asList(LOWEST, HIGHEST, SD, HD, FHD, UHD));
+
+ /** Quality constants with size from large to small. */
+ private static final List<Quality> QUALITIES_ORDER_BY_SIZE =
+ Arrays.asList(UHD, FHD, HD, SD);
+
+ static boolean containsQuality(@NonNull Quality quality) {
+ return QUALITIES.contains(quality);
+ }
+
+ /**
+ * Gets all video quality constants with clearly defined size sorted from largest to smallest.
+ *
+ * <p>{@link #HIGHEST} and {@link #LOWEST} are not included.
+ */
+ @NonNull
+ static List<Quality> getSortedQualities() {
+ return new ArrayList<>(QUALITIES_ORDER_BY_SIZE);
+ }
+
+ @AutoValue
+ abstract static class ConstantQuality extends Quality {
+ @NonNull
+ static ConstantQuality of(int value, @NonNull String name) {
+ return new AutoValue_Quality_ConstantQuality(value, name);
+ }
+
+ abstract int getValue();
+
+ @NonNull
+ abstract String getName();
+ }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/QualitySelector.java b/camera/camera-video/src/main/java/androidx/camera/video/QualitySelector.java
index b76cd4d..08bca34 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/QualitySelector.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/QualitySelector.java
@@ -16,22 +16,16 @@
package androidx.camera.video;
-import android.media.CamcorderProfile;
import android.util.Size;
-import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.Logger;
import androidx.camera.core.impl.CamcorderProfileProxy;
import androidx.core.util.Preconditions;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -45,192 +39,57 @@
* {@link Recorder.Builder#setQualitySelector(QualitySelector) Recorder}.
*
* <p>There are pre-defined quality constants that are universally used for video, such as
- * {@link #QUALITY_SD}, {@link #QUALITY_HD}, {@link #QUALITY_FHD} and {@link #QUALITY_UHD}, but
+ * {@link Quality#SD}, {@link Quality#HD}, {@link Quality#FHD} and {@link Quality#UHD}, but
* not all of them are supported on every device since each device has its own capabilities.
- * {@link #isQualitySupported(CameraInfo, int)} can be used to check whether a quality is
- * supported on the device or not and {@link #getResolution(CameraInfo, int)} can be used to get
+ * {@link #isQualitySupported(CameraInfo, Quality)} can be used to check whether a quality is
+ * supported on the device or not and {@link #getResolution(CameraInfo, Quality)} can be used to get
* the actual resolution defined in the device. Aside from checking the qualities one by one,
* QualitySelector provides a more convenient way to select a quality. The typical usage of
* selecting a single desired quality is:
* <pre>{@code
- * QualitySelector qualitySelector = QualitySelector.of(QualitySelector.QUALITY_FHD)
+ * QualitySelector qualitySelector = QualitySelector.from(Quality.FHD);
* }</pre>
- * Or the usage of selecting a series of qualities by desired order:
+ * or the usage of selecting a series of qualities by desired order:
* <pre>{@code
- * QualitySelector qualitySelector = QualitySelector
- * .firstTry(QualitySelector.QUALITY_FHD)
- * .thenTry(QualitySelector.QUALITY_HD)
- * .finallyTry(QualitySelector.QUALITY_HIGHEST)
+ * QualitySelector qualitySelector = QualitySelector.fromOrderedList(
+ * Arrays.asList(Quality.FHD, Quality.HD, Quality.HIGHEST)
+ * );
* }</pre>
- * The recommended way to set the {@link Procedure#finallyTry(int)} is giving guaranteed supported
- * qualities such as {@link #QUALITY_LOWEST} or {@link #QUALITY_HIGHEST}, which ensures the
+ * The recommended way is giving a guaranteed supported quality such as {@link Quality#LOWEST} or
+ * {@link Quality#HIGHEST} in the end of the desired quality list, which ensures the
* QualitySelector can always choose a supported quality. Another way to ensure a quality will be
* selected when none of the desired qualities are supported is to use
- * {@link Procedure#finallyTry(int, int)} with an open-ended fallback strategy such as
- * {@link #FALLBACK_STRATEGY_LOWER}:
+ * {@link #fromOrderedList(List, FallbackStrategy)} with an open-ended fallback strategy such as
+ * a fallback strategy from {@link FallbackStrategy#lowerQualityOrHigherThan(Quality)}:
* <pre>{@code
- * QualitySelector qualitySelector = QualitySelector
- * .firstTry(QualitySelector.QUALITY_UHD)
- * .finallyTry(QualitySelector.QUALITY_FHD, QualitySelector.FALLBACK_STRATEGY_LOWER)
+ * QualitySelector qualitySelector = QualitySelector.fromOrderedList(
+ * Arrays.asList(Quality.UHD, Quality.FHD),
+ * FallbackStrategy.lowerQualityOrHigherThan(Quality.FHD)
+ * );
* }</pre>
- * If QUALITY_UHD and QUALITY_FHD are not supported on the device, QualitySelector will select
- * the quality that is closest to and lower than QUALITY_FHD. If no lower quality is supported,
- * the quality that is closest to and higher than QUALITY_FHD will be selected.
+ * If UHD and FHD are not supported on the device, QualitySelector will select the quality that
+ * is closest to and lower than FHD. If no lower quality is supported, the quality that is
+ * closest to and higher than FHD will be selected.
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-public class QualitySelector {
+public final class QualitySelector {
private static final String TAG = "QualitySelector";
/**
- * A non-applicable quality.
- *
- * <p>Checking QUALITY_NONE via {@link #isQualitySupported(CameraInfo, int)} will return
- * {@code false}.
- *
- * @hide
- */
- @RestrictTo(Scope.LIBRARY)
- public static final int QUALITY_NONE = -1;
- /**
- * The lowest video quality supported by the video frame producer.
- */
- public static final int QUALITY_LOWEST = CamcorderProfile.QUALITY_LOW;
- /**
- * The highest video quality supported by the video frame producer.
- */
- public static final int QUALITY_HIGHEST = CamcorderProfile.QUALITY_HIGH;
- /**
- * Standard Definition (SD) 480p video quality.
- *
- * <p>This video quality usually corresponds to a resolution of 720 x 480 (480p) pixels.
- */
- public static final int QUALITY_SD = CamcorderProfile.QUALITY_480P;
- /**
- * High Definition (HD) video quality.
- *
- * <p>This video quality usually corresponds to a resolution of 1280 x 720 (720p) pixels.
- */
- public static final int QUALITY_HD = CamcorderProfile.QUALITY_720P;
- /**
- * Full High Definition (FHD) 1080p video quality.
- *
- * <p>This video quality usually corresponds to a resolution of 1920 x 1080 (1080p) pixels.
- */
- public static final int QUALITY_FHD = CamcorderProfile.QUALITY_1080P;
- /**
- * Ultra High Definition (UHD) 2160p video quality.
- *
- * <p>This video quality usually corresponds to a resolution of 3840 x 2160 (2160p) pixels.
- */
- public static final int QUALITY_UHD = CamcorderProfile.QUALITY_2160P;
-
- /** @hide */
- @IntDef({QUALITY_NONE, QUALITY_LOWEST, QUALITY_HIGHEST, QUALITY_SD, QUALITY_HD, QUALITY_FHD,
- QUALITY_UHD})
- @Retention(RetentionPolicy.SOURCE)
- @RestrictTo(Scope.LIBRARY)
- public @interface VideoQuality {
- }
-
- /** All quality constants. */
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- static final List<Integer> QUALITIES = Arrays.asList(QUALITY_NONE, QUALITY_LOWEST,
- QUALITY_HIGHEST, QUALITY_SD, QUALITY_HD, QUALITY_FHD, QUALITY_UHD);
-
- /** Quality constants with size from large to small. */
- private static final List<Integer> QUALITIES_ORDER_BY_SIZE = Arrays.asList(QUALITY_UHD,
- QUALITY_FHD, QUALITY_HD, QUALITY_SD);
-
- /**
- * The strategy that no fallback strategy will be applied.
- *
- * @hide
- */
- @RestrictTo(Scope.LIBRARY)
- public static final int FALLBACK_STRATEGY_NONE = 0;
-
- /**
- * Choose the quality that is closest to and higher than the desired quality. If that can not
- * result in a supported quality, choose the quality that is closest to and lower than the
- * desired quality.
- */
- public static final int FALLBACK_STRATEGY_HIGHER = 1;
-
- /**
- * Choose the quality that is closest to and higher than the desired quality.
- */
- public static final int FALLBACK_STRATEGY_STRICTLY_HIGHER = 2;
-
- /**
- * Choose the quality that is closest to and lower than the desired quality. If that can not
- * result in a supported quality, choose the quality that is closest to and higher than the
- * desired quality.
- */
- public static final int FALLBACK_STRATEGY_LOWER = 3;
-
- /**
- * Choose the quality that is closest to and lower than the desired quality.
- */
- public static final int FALLBACK_STRATEGY_STRICTLY_LOWER = 4;
-
- private static final int FALLBACK_STRATEGY_START = FALLBACK_STRATEGY_NONE;
- private static final int FALLBACK_STRATEGY_END = FALLBACK_STRATEGY_STRICTLY_LOWER;
-
- /**
- * The fallback strategies when desired quality is not supported.
- *
- * @hide
- */
- @RestrictTo(Scope.LIBRARY)
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({FALLBACK_STRATEGY_NONE,
- FALLBACK_STRATEGY_HIGHER,
- FALLBACK_STRATEGY_STRICTLY_HIGHER,
- FALLBACK_STRATEGY_LOWER,
- FALLBACK_STRATEGY_STRICTLY_LOWER
- })
- public @interface FallbackStrategy {
- }
-
- /**
- * Checks if the input quality is one of video quality constants.
- *
- * @hide
- */
- @RestrictTo(Scope.LIBRARY)
- public static boolean containsQuality(int quality) {
- return QUALITIES.contains(quality);
- }
-
- /**
- * Gets all video quality constants with clearly defined size sorted from largest to smallest.
- *
- * <p>{@link #QUALITY_NONE}, {@link #QUALITY_HIGHEST} and {@link #QUALITY_LOWEST} are not
- * included.
- *
- * @hide
- */
- @RestrictTo(Scope.LIBRARY)
- @NonNull
- public static List<Integer> getSortedQualities() {
- return new ArrayList<>(QUALITIES_ORDER_BY_SIZE);
- }
-
- /**
* Gets all supported qualities on the device.
*
* <p>The returned list is sorted by quality size from largest to smallest. For the qualities in
* the returned list, with the same input cameraInfo,
- * {@link #isQualitySupported(CameraInfo, int)} will return {@code true} and
- * {@link #getResolution(CameraInfo, int)} will return the corresponding resolution.
+ * {@link #isQualitySupported(CameraInfo, Quality)} will return {@code true} and
+ * {@link #getResolution(CameraInfo, Quality)} will return the corresponding resolution.
*
- * <p>Note: Constants {@link #QUALITY_HIGHEST} and {@link #QUALITY_LOWEST} are not included
+ * <p>Note: Constants {@link Quality#HIGHEST} and {@link Quality#LOWEST} are not included
* in the returned list, but their corresponding qualities are included.
*
* @param cameraInfo the cameraInfo
*/
@NonNull
- public static List<Integer> getSupportedQualities(@NonNull CameraInfo cameraInfo) {
+ public static List<Quality> getSupportedQualities(@NonNull CameraInfo cameraInfo) {
return VideoCapabilities.from(cameraInfo).getSupportedQualities();
}
@@ -240,11 +99,11 @@
* <p>Calling this method with one of the qualities contained in the returned list of
* {@link #getSupportedQualities} will return {@code true}.
*
- * <p>Possible values for {@code quality} include {@link #QUALITY_LOWEST},
- * {@link #QUALITY_HIGHEST}, {@link #QUALITY_SD}, {@link #QUALITY_HD}, {@link #QUALITY_FHD}
- * and {@link #QUALITY_UHD}.
+ * <p>Possible values for {@code quality} include {@link Quality#LOWEST},
+ * {@link Quality#HIGHEST}, {@link Quality#SD}, {@link Quality#HD}, {@link Quality#FHD}
+ * and {@link Quality#UHD}.
*
- * <p>If this method is called with {@link #QUALITY_LOWEST} or {@link #QUALITY_HIGHEST}, it
+ * <p>If this method is called with {@link Quality#LOWEST} or {@link Quality#HIGHEST}, it
* will return {@code true} except the case that none of the qualities can be supported.
*
* @param cameraInfo the cameraInfo for checking the quality.
@@ -253,129 +112,148 @@
* @see #getSupportedQualities(CameraInfo)
*/
public static boolean isQualitySupported(@NonNull CameraInfo cameraInfo,
- @VideoQuality int quality) {
+ @NonNull Quality quality) {
return VideoCapabilities.from(cameraInfo).isQualitySupported(quality);
}
/**
* Gets the corresponding resolution from the input quality.
*
- * <p>Possible values for {@code quality} include {@link #QUALITY_LOWEST},
- * {@link #QUALITY_HIGHEST}, {@link #QUALITY_SD}, {@link #QUALITY_HD}, {@link #QUALITY_FHD}
- * and {@link #QUALITY_UHD}.
+ * <p>Possible values for {@code quality} include {@link Quality#LOWEST},
+ * {@link Quality#HIGHEST}, {@link Quality#SD}, {@link Quality#HD}, {@link Quality#FHD}
+ * and {@link Quality#UHD}.
*
* @param cameraInfo the cameraInfo for checking the quality.
* @param quality one of the quality constants.
* @return the corresponding resolution from the input quality, or {@code null} if the
- * quality is not supported on the device. {@link #isQualitySupported(CameraInfo, int)} can
+ * quality is not supported on the device. {@link #isQualitySupported(CameraInfo, Quality)} can
* be used to check if the input quality is supported.
- * @throws IllegalArgumentException if not a quality constant
+ * @throws IllegalArgumentException if quality is not one of the possible values.
* @see #isQualitySupported
*/
@Nullable
- public static Size getResolution(@NonNull CameraInfo cameraInfo, @VideoQuality int quality) {
+ public static Size getResolution(@NonNull CameraInfo cameraInfo, @NonNull Quality quality) {
checkQualityConstantsOrThrow(quality);
CamcorderProfileProxy profile = VideoCapabilities.from(cameraInfo).getProfile(quality);
return profile != null ? new Size(profile.getVideoFrameWidth(),
profile.getVideoFrameHeight()) : null;
}
- private final List<Integer> mPreferredQualityList;
- @VideoQuality
- private final int mFallbackQuality;
- @FallbackStrategy
- private final int mFallbackStrategy;
+ private final List<Quality> mPreferredQualityList;
+ private final FallbackStrategy mFallbackStrategy;
- QualitySelector(@NonNull List<Integer> preferredQualityList,
- @VideoQuality int fallbackQuality,
- @FallbackStrategy int fallbackStrategy) {
- Preconditions.checkArgument(preferredQualityList.size() > 0, "No preferred quality.");
- mPreferredQualityList = Collections.unmodifiableList(preferredQualityList);
- mFallbackQuality = fallbackQuality;
+ QualitySelector(@NonNull List<Quality> preferredQualityList,
+ @NonNull FallbackStrategy fallbackStrategy) {
+ Preconditions.checkArgument(
+ !preferredQualityList.isEmpty() || fallbackStrategy != FallbackStrategy.NONE,
+ "No preferred quality and fallback strategy.");
+ mPreferredQualityList = Collections.unmodifiableList(new ArrayList<>(preferredQualityList));
mFallbackStrategy = fallbackStrategy;
}
/**
- * Sets the desired quality with the highest priority.
+ * Gets an instance of QualitySelector with a desired quality.
*
- * <p>This method initiates a procedure for specifying the requirements of selecting
- * qualities. Other requirements can be further added with {@link Procedure} methods.
- *
- * @param quality the quality constant. Possible values include {@link #QUALITY_LOWEST},
- * {@link #QUALITY_HIGHEST}, {@link #QUALITY_SD}, {@link #QUALITY_HD}, {@link #QUALITY_FHD},
- * or {@link #QUALITY_UHD}.
- * @return the {@link Procedure} for specifying quality selection requirements.
- * @throws IllegalArgumentException if the given quality is not a quality constant.
- * @see Procedure
+ * @param quality the quality. Possible values include {@link Quality#LOWEST},
+ * {@link Quality#HIGHEST}, {@link Quality#SD}, {@link Quality#HD}, {@link Quality#FHD},
+ * or {@link Quality#UHD}.
+ * @return the QualitySelector instance.
+ * @throws NullPointerException if {@code quality} is {@code null}.
+ * @throws IllegalArgumentException if {@code quality} is not one of the possible values.
*/
@NonNull
- public static Procedure firstTry(@VideoQuality int quality) {
- return new Procedure(quality);
+ public static QualitySelector from(@NonNull Quality quality) {
+ return from(quality, FallbackStrategy.NONE);
}
/**
- * Gets an instance of QualitySelector with only one desired quality.
+ * Gets an instance of QualitySelector with a desired quality and a fallback strategy.
*
- * <p>If there are more than one desired qualities, use {@link #firstTry} for further settings.
+ * <p>If the quality is not supported, the fallback strategy will be applied. The fallback
+ * strategy can be created by {@link FallbackStrategy} API such as
+ * {@link FallbackStrategy#lowerQualityThan(Quality)}.
*
- * @param quality the quality constant. Possible values include {@link #QUALITY_LOWEST},
- * {@link #QUALITY_HIGHEST}, {@link #QUALITY_SD}, {@link #QUALITY_HD}, {@link #QUALITY_FHD},
- * or {@link #QUALITY_UHD}.
+ * @param quality the quality. Possible values include {@link Quality#LOWEST},
+ * {@link Quality#HIGHEST}, {@link Quality#SD}, {@link Quality#HD}, {@link Quality#FHD},
+ * or {@link Quality#UHD}.
+ * @param fallbackStrategy the fallback strategy that will be applied when the device does
+ * not support {@code quality}.
* @return the QualitySelector instance.
- * @throws IllegalArgumentException if the given quality is not a quality constant.
+ * @throws NullPointerException if {@code quality} is {@code null} or {@code fallbackStrategy}
+ * is {@code null}.
+ * @throws IllegalArgumentException if {@code quality} is not one of the possible values.
*/
@NonNull
- public static QualitySelector of(@VideoQuality int quality) {
- return of(quality, FALLBACK_STRATEGY_NONE);
+ public static QualitySelector from(@NonNull Quality quality,
+ @NonNull FallbackStrategy fallbackStrategy) {
+ Preconditions.checkNotNull(quality, "quality cannot be null");
+ Preconditions.checkNotNull(fallbackStrategy, "fallbackStrategy cannot be null");
+ checkQualityConstantsOrThrow(quality);
+ return new QualitySelector(Arrays.asList(quality), fallbackStrategy);
}
/**
- * Gets an instance of QualitySelector with only one desired quality and a fallback strategy.
+ * Gets an instance of QualitySelector with ordered desired qualities.
*
- * <p>If there are more than one desired qualities, use {@link #firstTry} for further settings.
+ * <p>The final quality will be selected according to the order in the quality list.
*
- * @param quality the quality constant. Possible values include {@link #QUALITY_LOWEST},
- * {@link #QUALITY_HIGHEST}, {@link #QUALITY_SD}, {@link #QUALITY_HD}, {@link #QUALITY_FHD},
- * or {@link #QUALITY_UHD}.
- * @param fallbackStrategy the fallback strategy. Possible values include
- * {@link #FALLBACK_STRATEGY_HIGHER}, {@link #FALLBACK_STRATEGY_STRICTLY_HIGHER},
- * {@link #FALLBACK_STRATEGY_LOWER} and {@link #FALLBACK_STRATEGY_STRICTLY_LOWER}.
+ * @param qualities the quality list. Possible values include {@link Quality#LOWEST},
+ * {@link Quality#HIGHEST}, {@link Quality#SD}, {@link Quality#HD}, {@link Quality#FHD},
+ * or {@link Quality#UHD}.
* @return the QualitySelector instance.
- * @throws IllegalArgumentException if {@code quality} is not a quality constant or
- * {@code fallbackStrategy} is not a fallback strategy constant.
+ * @throws NullPointerException if {@code qualities} is {@code null}.
+ * @throws IllegalArgumentException if {@code qualities} is empty or contains a quality that is
+ * not one of the possible values, including a {@code null} value.
*/
@NonNull
- public static QualitySelector of(@VideoQuality int quality,
- @FallbackStrategy int fallbackStrategy) {
- return firstTry(quality).finallyTry(quality, fallbackStrategy);
+ public static QualitySelector fromOrderedList(@NonNull List<Quality> qualities) {
+ return fromOrderedList(qualities, FallbackStrategy.NONE);
+ }
+
+ /**
+ * Gets an instance of QualitySelector with ordered desired qualities and a fallback strategy.
+ *
+ * <p>The final quality will be selected according to the order in the quality list.
+ * If no quality is supported, the fallback strategy will be applied. The fallback
+ * strategy can be created by {@link FallbackStrategy} API such as
+ * {@link FallbackStrategy#lowerQualityThan(Quality)}.
+ *
+ * @param qualities the quality list. Possible values include {@link Quality#LOWEST},
+ * {@link Quality#HIGHEST}, {@link Quality#SD}, {@link Quality#HD}, {@link Quality#FHD},
+ * or {@link Quality#UHD}.
+ * @param fallbackStrategy the fallback strategy that will be applied when the device does
+ * not support those {@code qualities}.
+ * @throws NullPointerException if {@code qualities} is {@code null} or
+ * {@code fallbackStrategy} is {@code null}.
+ * @throws IllegalArgumentException if {@code qualities} is empty or contains a quality that is
+ * not one of the possible values, including a {@code null} value.
+ */
+ @NonNull
+ public static QualitySelector fromOrderedList(@NonNull List<Quality> qualities,
+ @NonNull FallbackStrategy fallbackStrategy) {
+ Preconditions.checkNotNull(qualities, "qualities cannot be null");
+ Preconditions.checkNotNull(fallbackStrategy, "fallbackStrategy cannot be null");
+ Preconditions.checkArgument(!qualities.isEmpty(), "qualities cannot be empty");
+ checkQualityConstantsOrThrow(qualities);
+ return new QualitySelector(qualities, fallbackStrategy);
}
/**
* Generates a sorted quality list that matches the desired quality settings.
*
* <p>The method bases on the desired qualities and the fallback strategy to find a supported
- * quality list on this device. The desired qualities can be set by a series of try methods such
- * as {@link #firstTry(int)}, {@link #of(int)}, {@link Procedure#thenTry(int)} and
- * {@link Procedure#finallyTry(int)}. The fallback strategy can be set via
- * {@link #of(int, int)} and {@link Procedure#finallyTry(int, int)}. If no fallback strategy
- * is specified, {@link #FALLBACK_STRATEGY_NONE} will be applied by default.
- *
- * <p>The search algorithm first checks which desired quality is supported according to the
- * set sequence and adds to the returned list by order. Then the fallback strategy will be
- * applied to add more valid qualities.
+ * quality list on this device. The search algorithm first checks which desired quality is
+ * supported according to the set sequence and adds to the returned list by order. Then the
+ * fallback strategy will be applied to add more valid qualities.
*
* @param cameraInfo the cameraInfo for checking the quality.
* @return a sorted supported quality list according to the desired quality settings.
- * @see Procedure
- *
- * @hide
*/
- @RestrictTo(Scope.LIBRARY)
@NonNull
- public List<Integer> getPrioritizedQualities(@NonNull CameraInfo cameraInfo) {
+ List<Quality> getPrioritizedQualities(@NonNull CameraInfo cameraInfo) {
VideoCapabilities videoCapabilities = VideoCapabilities.from(cameraInfo);
- List<Integer> supportedQualities = videoCapabilities.getSupportedQualities();
+ List<Quality> supportedQualities = videoCapabilities.getSupportedQualities();
if (supportedQualities.isEmpty()) {
Logger.w(TAG, "No supported quality on the device.");
return new ArrayList<>();
@@ -383,23 +261,25 @@
Logger.d(TAG, "supportedQualities = " + supportedQualities);
// Use LinkedHashSet to prevent from duplicate quality and keep the adding order.
- Set<Integer> sortedQualities = new LinkedHashSet<>();
+ Set<Quality> sortedQualities = new LinkedHashSet<>();
// Add exact quality.
- for (Integer quality : mPreferredQualityList) {
- if (quality == QUALITY_HIGHEST) {
+ for (Quality quality : mPreferredQualityList) {
+ if (quality == Quality.HIGHEST) {
// Highest means user want a quality as higher as possible, so the return list can
// contain all supported resolutions from large to small.
sortedQualities.addAll(supportedQualities);
break;
- } else if (quality == QUALITY_LOWEST) {
+ } else if (quality == Quality.LOWEST) {
// Opposite to the highest
- List<Integer> reversedList = new ArrayList<>(supportedQualities);
+ List<Quality> reversedList = new ArrayList<>(supportedQualities);
Collections.reverse(reversedList);
sortedQualities.addAll(reversedList);
break;
} else {
if (supportedQualities.contains(quality)) {
sortedQualities.add(quality);
+ } else {
+ Logger.w(TAG, "quality is not supported and will be ignored: " + quality);
}
}
}
@@ -415,13 +295,12 @@
public String toString() {
return "QualitySelector{"
+ "preferredQualities=" + mPreferredQualityList
- + ", fallbackQuality=" + mFallbackQuality
+ ", fallbackStrategy=" + mFallbackStrategy
+ "}";
}
- private void addByFallbackStrategy(@NonNull List<Integer> supportedQualities,
- @NonNull Set<Integer> priorityQualities) {
+ private void addByFallbackStrategy(@NonNull List<Quality> supportedQualities,
+ @NonNull Set<Quality> priorityQualities) {
if (supportedQualities.isEmpty()) {
return;
}
@@ -430,42 +309,45 @@
// fallback strategy.
return;
}
- Logger.d(TAG, "Select quality by fallbackStrategy = " + mFallbackStrategy
- + " on fallback quality = " + mFallbackQuality);
+ Logger.d(TAG, "Select quality by fallbackStrategy = " + mFallbackStrategy);
// No fallback strategy, return directly.
- if (mFallbackStrategy == QualitySelector.FALLBACK_STRATEGY_NONE) {
+ if (mFallbackStrategy == FallbackStrategy.NONE) {
return;
}
+ Preconditions.checkState(mFallbackStrategy instanceof FallbackStrategy.RuleStrategy,
+ "Currently only support type RuleStrategy");
+ FallbackStrategy.RuleStrategy fallbackStrategy =
+ (FallbackStrategy.RuleStrategy) mFallbackStrategy;
// Note that fallback quality could be an unsupported quality, so all quality constants
// need to be loaded to find the position of fallback quality.
// The list returned from getSortedQualities() is sorted from large to small.
- List<Integer> sizeSortedQualities = getSortedQualities();
- int fallbackQuality;
- if (mFallbackQuality == QUALITY_HIGHEST) {
+ List<Quality> sizeSortedQualities = Quality.getSortedQualities();
+ Quality fallbackQuality;
+ if (fallbackStrategy.getFallbackQuality() == Quality.HIGHEST) {
fallbackQuality = sizeSortedQualities.get(0);
- } else if (mFallbackQuality == QUALITY_LOWEST) {
+ } else if (fallbackStrategy.getFallbackQuality() == Quality.LOWEST) {
fallbackQuality = sizeSortedQualities.get(sizeSortedQualities.size() - 1);
} else {
- fallbackQuality = mFallbackQuality;
+ fallbackQuality = fallbackStrategy.getFallbackQuality();
}
int index = sizeSortedQualities.indexOf(fallbackQuality);
Preconditions.checkState(index != -1); // Should not happen.
// search larger supported quality
- List<Integer> largerQualities = new ArrayList<>();
+ List<Quality> largerQualities = new ArrayList<>();
for (int i = index - 1; i >= 0; i--) {
- int quality = sizeSortedQualities.get(i);
+ Quality quality = sizeSortedQualities.get(i);
if (supportedQualities.contains(quality)) {
largerQualities.add(quality);
}
}
// search smaller supported quality
- List<Integer> smallerQualities = new ArrayList<>();
+ List<Quality> smallerQualities = new ArrayList<>();
for (int i = index + 1; i < sizeSortedQualities.size(); i++) {
- int quality = sizeSortedQualities.get(i);
+ Quality quality = sizeSortedQualities.get(i);
if (supportedQualities.contains(quality)) {
smallerQualities.add(quality);
}
@@ -476,22 +358,22 @@
+ ", largerQualities = " + largerQualities
+ ", smallerQualities = " + smallerQualities);
- switch (mFallbackStrategy) {
- case QualitySelector.FALLBACK_STRATEGY_HIGHER:
+ switch (fallbackStrategy.getFallbackRule()) {
+ case FallbackStrategy.FALLBACK_RULE_HIGHER_OR_LOWER:
priorityQualities.addAll(largerQualities);
priorityQualities.addAll(smallerQualities);
break;
- case QualitySelector.FALLBACK_STRATEGY_STRICTLY_HIGHER:
+ case FallbackStrategy.FALLBACK_RULE_HIGHER:
priorityQualities.addAll(largerQualities);
break;
- case QualitySelector.FALLBACK_STRATEGY_LOWER:
+ case FallbackStrategy.FALLBACK_RULE_LOWER_OR_HIGHER:
priorityQualities.addAll(smallerQualities);
priorityQualities.addAll(largerQualities);
break;
- case QualitySelector.FALLBACK_STRATEGY_STRICTLY_LOWER:
+ case FallbackStrategy.FALLBACK_RULE_LOWER:
priorityQualities.addAll(smallerQualities);
break;
- case QualitySelector.FALLBACK_STRATEGY_NONE:
+ case FallbackStrategy.FALLBACK_RULE_NONE:
// No-Op
break;
default:
@@ -499,108 +381,15 @@
}
}
- @SuppressWarnings("WeakerAccess") /* synthetic access */
- static void checkQualityConstantsOrThrow(@QualitySelector.VideoQuality int quality) {
- Preconditions.checkArgument(QualitySelector.containsQuality(quality),
- "Unknown quality: " + quality);
+ private static void checkQualityConstantsOrThrow(@NonNull List<Quality> qualities) {
+ for (Quality quality : qualities) {
+ Preconditions.checkArgument(Quality.containsQuality(quality),
+ "qualities contain invalid quality: " + quality);
+ }
}
- /**
- * Procedure can be used to build {@link QualitySelector} with further settings.
- *
- * <p>A QualitySelector can be simply created by {@link QualitySelector#of QualitySelector.of}
- * with a single desired quality. When more quality settings are needed, use
- * {@link QualitySelector#firstTry} to get a Procedure, chain the desired qualities by
- * {@link #thenTry} and generate the QualitySelector by {@link #finallyTry} with or without
- * fallback strategy. For example:
- *
- * <pre>{@code
- * QualitySelector qualitySelector = QualitySelector
- * .firstTry(QualitySelector.QUALITY_UHD)
- * .thenTry(QualitySelector.QUALITY_HD)
- * .finallyTry(QualitySelector.QUALITY_SD)
- * }</pre>
- * or
- <pre>{@code
- * QualitySelector qualitySelector = QualitySelector
- * .firstTry(QualitySelector.QUALITY_UHD)
- * .finallyTry(QualitySelector.QUALITY_HD,
- * QualitySelector.FALLBACK_STRATEGY_STRICTLY_LOWER)
- * }</pre>
- */
- public static class Procedure {
- private final List<Integer> mPreferredQualityList = new ArrayList<>();
-
- Procedure(int quality) {
- addQuality(quality);
- }
-
- /**
- * Adds a quality candidate.
- *
- * @param quality the quality constant. Possible values include {@link #QUALITY_LOWEST},
- * {@link #QUALITY_HIGHEST}, {@link #QUALITY_SD}, {@link #QUALITY_HD},
- * {@link #QUALITY_FHD} or {@link #QUALITY_UHD}.
- * @return the procedure that can continue to be set
- * @throws IllegalArgumentException if the given quality is not a quality constant
- */
- @NonNull
- public Procedure thenTry(@VideoQuality int quality) {
- addQuality(quality);
- return this;
- }
-
- /**
- * Sets the final desired quality.
- *
- * <p>This method finishes the setting procedure and generates a {@link QualitySelector}
- * with the requirements set to the procedure.
- *
- * @param quality the quality constant. Possible values include {@link #QUALITY_LOWEST},
- * {@link #QUALITY_HIGHEST}, {@link #QUALITY_SD}, {@link #QUALITY_HD},
- * {@link #QUALITY_FHD} or {@link #QUALITY_UHD}.
- * @return the {@link QualitySelector}.
- * @throws IllegalArgumentException if the given quality is not a quality constant
- */
- @NonNull
- public QualitySelector finallyTry(@VideoQuality int quality) {
- return finallyTry(quality, FALLBACK_STRATEGY_NONE);
- }
-
- /**
- * Sets the final desired quality and fallback strategy.
- *
- * <p>The fallback strategy will be applied on this quality when all desired qualities are
- * not supported.
- *
- * <p>This method finishes the setting procedure and generates a {@link QualitySelector}
- * with the requirements set to the procedure.
- *
- * @param quality the quality constant. Possible values include {@link #QUALITY_LOWEST},
- * {@link #QUALITY_HIGHEST}, {@link #QUALITY_SD}, {@link #QUALITY_HD},
- * {@link #QUALITY_FHD} or {@link #QUALITY_UHD}.
- * @param fallbackStrategy the fallback strategy. Possible values include
- * {@link #FALLBACK_STRATEGY_HIGHER}, {@link #FALLBACK_STRATEGY_STRICTLY_HIGHER},
- * {@link #FALLBACK_STRATEGY_LOWER} and {@link #FALLBACK_STRATEGY_STRICTLY_LOWER}.
- * @return the {@link QualitySelector}.
- * @throws IllegalArgumentException if {@code quality} is not a quality constant or
- * {@code fallbackStrategy} is not a fallback strategy constant.
- */
- @NonNull
- public QualitySelector finallyTry(@VideoQuality int quality,
- @FallbackStrategy int fallbackStrategy) {
- Preconditions.checkArgument(fallbackStrategy >= FALLBACK_STRATEGY_START
- && fallbackStrategy <= FALLBACK_STRATEGY_END,
- "The value must be a fallback strategy constant.");
- addQuality(quality);
- return new QualitySelector(new ArrayList<>(mPreferredQualityList), quality,
- fallbackStrategy);
- }
-
- private void addQuality(@VideoQuality int quality) {
- checkQualityConstantsOrThrow(quality);
- Preconditions.checkArgument(quality != QUALITY_NONE, "Unsupported quality: " + quality);
- mPreferredQualityList.add(quality);
- }
+ private static void checkQualityConstantsOrThrow(@NonNull Quality quality) {
+ Preconditions.checkArgument(Quality.containsQuality(quality),
+ "Invalid quality: " + quality);
}
}
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 99a635a9..27d260e7 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
@@ -16,10 +16,6 @@
package androidx.camera.video;
-import static androidx.camera.video.QualitySelector.FALLBACK_STRATEGY_HIGHER;
-import static androidx.camera.video.QualitySelector.QUALITY_FHD;
-import static androidx.camera.video.QualitySelector.QUALITY_HD;
-import static androidx.camera.video.QualitySelector.QUALITY_SD;
import static androidx.camera.video.VideoRecordEvent.Finalize.ERROR_ENCODING_FAILED;
import static androidx.camera.video.VideoRecordEvent.Finalize.ERROR_FILE_SIZE_LIMIT_REACHED;
import static androidx.camera.video.VideoRecordEvent.Finalize.ERROR_INVALID_OUTPUT_OPTIONS;
@@ -34,8 +30,8 @@
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
-import android.media.MediaCodecInfo;
import android.media.MediaMuxer;
+import android.media.MediaRecorder;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
@@ -53,6 +49,7 @@
import androidx.camera.core.AspectRatio;
import androidx.camera.core.Logger;
import androidx.camera.core.SurfaceRequest;
+import androidx.camera.core.impl.CamcorderProfileProxy;
import androidx.camera.core.impl.MutableStateObservable;
import androidx.camera.core.impl.Observable;
import androidx.camera.core.impl.StateObservable;
@@ -63,18 +60,23 @@
import androidx.camera.core.impl.utils.futures.Futures;
import androidx.camera.video.internal.AudioSource;
import androidx.camera.video.internal.AudioSourceAccessException;
-import androidx.camera.video.internal.BufferProvider;
import androidx.camera.video.internal.ResourceCreationException;
import androidx.camera.video.internal.compat.Api26Impl;
import androidx.camera.video.internal.compat.quirk.DeactivateEncoderSurfaceBeforeStopEncoderQuirk;
import androidx.camera.video.internal.compat.quirk.DeviceQuirks;
+import androidx.camera.video.internal.config.AudioEncoderConfigCamcorderProfileResolver;
+import androidx.camera.video.internal.config.AudioEncoderConfigDefaultResolver;
+import androidx.camera.video.internal.config.AudioSourceSettingsCamcorderProfileResolver;
+import androidx.camera.video.internal.config.AudioSourceSettingsDefaultResolver;
+import androidx.camera.video.internal.config.MimeInfo;
+import androidx.camera.video.internal.config.VideoEncoderConfigCamcorderProfileResolver;
+import androidx.camera.video.internal.config.VideoEncoderConfigDefaultResolver;
import androidx.camera.video.internal.encoder.AudioEncoderConfig;
import androidx.camera.video.internal.encoder.EncodeException;
import androidx.camera.video.internal.encoder.EncodedData;
import androidx.camera.video.internal.encoder.Encoder;
import androidx.camera.video.internal.encoder.EncoderCallback;
import androidx.camera.video.internal.encoder.EncoderImpl;
-import androidx.camera.video.internal.encoder.InputBuffer;
import androidx.camera.video.internal.encoder.InvalidConfigException;
import androidx.camera.video.internal.encoder.OutputConfig;
import androidx.camera.video.internal.encoder.VideoEncoderConfig;
@@ -82,6 +84,7 @@
import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.core.util.Consumer;
import androidx.core.util.Preconditions;
+import androidx.core.util.Supplier;
import com.google.auto.value.AutoValue;
import com.google.common.util.concurrent.ListenableFuture;
@@ -89,6 +92,7 @@
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
@@ -234,29 +238,16 @@
* <p>The default quality selector chooses a video quality suitable for recordings based on
* device and compatibility constraints. It is equivalent to:
* <pre>{@code
- * QualitySelector.firstTry(QUALITY_FHD)
- * .thenTry(QUALITY_HD)
- * .thenTry(QUALITY_SD)
- * .finallyTry(QUALITY_FHD, FALLBACK_STRATEGY_HIGHER);
+ * QualitySelector.fromOrderedList(Arrays.asList(Quality.FHD, Quality.HD, Quality.SD),
+ * FallbackStrategy.higherQualityOrLowerThan(Quality.FHD));
* }</pre>
*
* @see QualitySelector
*/
public static final QualitySelector DEFAULT_QUALITY_SELECTOR =
- QualitySelector.firstTry(QUALITY_FHD)
- .thenTry(QUALITY_HD)
- .thenTry(QUALITY_SD)
- .finallyTry(QUALITY_FHD, FALLBACK_STRATEGY_HIGHER);
+ QualitySelector.fromOrderedList(Arrays.asList(Quality.FHD, Quality.HD, Quality.SD),
+ FallbackStrategy.higherQualityOrLowerThan(Quality.FHD));
- private static final AudioSpec AUDIO_SPEC_DEFAULT =
- AudioSpec.builder()
- .setSourceFormat(
- AudioSpec.SOURCE_FORMAT_PCM_16BIT) /* Defaults to PCM_16BIT as it's
- guaranteed supported on devices. May consider allowing users to set
- format through AudioSpec later. */
- .setSource(AudioSpec.SOURCE_CAMCORDER)
- .setChannelCount(AudioSpec.CHANNEL_COUNT_MONO)
- .build();
private static final VideoSpec VIDEO_SPEC_DEFAULT =
VideoSpec.builder()
.setQualitySelector(DEFAULT_QUALITY_SELECTOR)
@@ -264,16 +255,9 @@
.build();
private static final MediaSpec MEDIA_SPEC_DEFAULT =
MediaSpec.builder()
- .setOutputFormat(MediaSpec.OUTPUT_FORMAT_MPEG_4)
- .setAudioSpec(AUDIO_SPEC_DEFAULT)
+ .setOutputFormat(MediaSpec.OUTPUT_FORMAT_AUTO)
.setVideoSpec(VIDEO_SPEC_DEFAULT)
.build();
- private static final int AUDIO_BITRATE_DEFAULT = 88200;
- // Default to 44100 for now as it's guaranteed supported on devices.
- private static final int AUDIO_SAMPLE_RATE_DEFAULT = 44100;
- private static final int VIDEO_FRAME_RATE_DEFAULT = 30;
- private static final int VIDEO_BITRATE_DEFAULT = 10 * 1024 * 1024; // 10M
- private static final int VIDEO_INTRA_FRAME_INTERVAL_DEFAULT = 1;
@SuppressWarnings("deprecation")
private static final String MEDIA_COLUMN = MediaStore.Video.Media.DATA;
private static final Exception PENDING_RECORDING_ERROR_CAUSE_SOURCE_INACTIVE =
@@ -328,6 +312,7 @@
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
boolean mInProgressRecordingStopping = false;
private SurfaceRequest.TransformationInfo mSurfaceTransformationInfo = null;
+ private CamcorderProfileProxy mResolvedCamcorderProfile = null;
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
final List<ListenableFuture<Void>> mEncodingFutures = new ArrayList<>();
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
@@ -978,10 +963,25 @@
});
onInitialized();
} else {
- setupVideo(surfaceRequest);
surfaceRequest.setTransformationInfoListener(mSequentialExecutor,
(transformationInfo) -> mSurfaceTransformationInfo =
transformationInfo);
+ Size surfaceSize = surfaceRequest.getResolution();
+ // Fetch and cache nearest camcorder profile, if one exists.
+ VideoCapabilities capabilities =
+ VideoCapabilities.from(surfaceRequest.getCamera().getCameraInfo());
+ Quality highestSupportedQuality =
+ capabilities.findHighestSupportedQualityFor(surfaceSize);
+ Logger.d(TAG, "Using supported quality of " + highestSupportedQuality
+ + " for surface size " + surfaceSize);
+ if (highestSupportedQuality != Quality.NONE) {
+ mResolvedCamcorderProfile = capabilities.getProfile(highestSupportedQuality);
+ if (mResolvedCamcorderProfile == null) {
+ throw new AssertionError("Camera advertised available quality but did not "
+ + "produce CamcorderProfile for advertised quality.");
+ }
+ }
+ setupVideo(surfaceRequest);
}
}
@@ -1028,24 +1028,6 @@
@NonNull
private MediaSpec composeRecorderMediaSpec(@NonNull MediaSpec mediaSpec) {
MediaSpec.Builder mediaSpecBuilder = mediaSpec.toBuilder();
- if (mediaSpec.getOutputFormat() == MediaSpec.OUTPUT_FORMAT_AUTO) {
- mediaSpecBuilder.setOutputFormat(MEDIA_SPEC_DEFAULT.getOutputFormat());
- }
-
- // Append default audio configurations
- AudioSpec audioSpec = mediaSpec.getAudioSpec();
- if (audioSpec.getSourceFormat() == AudioSpec.SOURCE_FORMAT_AUTO) {
- mediaSpecBuilder.configureAudio(
- builder -> builder.setSourceFormat(AUDIO_SPEC_DEFAULT.getSourceFormat()));
- }
- if (audioSpec.getSource() == AudioSpec.SOURCE_AUTO) {
- mediaSpecBuilder.configureAudio(
- builder -> builder.setSource(AUDIO_SPEC_DEFAULT.getSource()));
- }
- if (audioSpec.getChannelCount() == AudioSpec.CHANNEL_COUNT_AUTO) {
- mediaSpecBuilder.configureAudio(
- builder -> builder.setChannelCount(AUDIO_SPEC_DEFAULT.getChannelCount()));
- }
// Append default video configurations
VideoSpec videoSpec = mediaSpec.getVideoSpec();
@@ -1068,29 +1050,146 @@
@ExecutedBy("mSequentialExecutor")
@NonNull
- private AudioEncoderConfig composeAudioEncoderConfig(@NonNull MediaSpec mediaSpec) {
- return AudioEncoderConfig.builder()
- .setMimeType(MediaSpec.outputFormatToAudioMime(mediaSpec.getOutputFormat()))
- .setBitrate(AUDIO_BITRATE_DEFAULT)
- .setSampleRate(selectSampleRate(mediaSpec.getAudioSpec()))
- .setChannelCount(mediaSpec.getAudioSpec().getChannelCount())
- .build();
+ private MimeInfo resolveAudioMimeInfo(@NonNull MediaSpec mediaSpec) {
+ String mediaSpecAudioMime = MediaSpec.outputFormatToAudioMime(mediaSpec.getOutputFormat());
+ int mediaSpecAudioProfile =
+ MediaSpec.outputFormatToAudioProfile(mediaSpec.getOutputFormat());
+ String resolvedAudioMime = mediaSpecAudioMime;
+ int resolvedAudioProfile = mediaSpecAudioProfile;
+ boolean camcorderProfileIsCompatible = false;
+ if (mResolvedCamcorderProfile != null) {
+ String camcorderProfileAudioMime = mResolvedCamcorderProfile.getAudioCodecMimeType();
+ int camcorderProfileAudioProfile = mResolvedCamcorderProfile.getRequiredAudioProfile();
+
+ if (camcorderProfileAudioMime == null) {
+ Logger.d(TAG, "CamcorderProfile contains undefined AUDIO mime type so cannot be "
+ + "used. May rely on fallback defaults to derive settings [chosen mime "
+ + "type: "
+ + resolvedAudioMime + "(profile: " + resolvedAudioProfile + ")]");
+ } else if (mediaSpec.getOutputFormat() == MediaSpec.OUTPUT_FORMAT_AUTO) {
+ camcorderProfileIsCompatible = true;
+ resolvedAudioMime = camcorderProfileAudioMime;
+ resolvedAudioProfile = camcorderProfileAudioProfile;
+ Logger.d(TAG, "MediaSpec contains OUTPUT_FORMAT_AUTO. Using CamcorderProfile "
+ + "to derive AUDIO settings [mime type: "
+ + resolvedAudioMime + "(profile: " + resolvedAudioProfile + ")]");
+ } else if (Objects.equals(mediaSpecAudioMime, camcorderProfileAudioMime)
+ && mediaSpecAudioProfile == camcorderProfileAudioProfile) {
+ camcorderProfileIsCompatible = true;
+ resolvedAudioMime = camcorderProfileAudioMime;
+ resolvedAudioProfile = camcorderProfileAudioProfile;
+ Logger.d(TAG, "MediaSpec audio mime/profile matches CamcorderProfile. "
+ + "Using CamcorderProfile to derive AUDIO settings [mime type: "
+ + resolvedAudioMime + "(profile: " + resolvedAudioProfile + ")]");
+ } else {
+ Logger.d(TAG, "MediaSpec audio mime or profile does not match CamcorderProfile, so "
+ + "CamcorderProfile settings cannot be used. May rely on fallback "
+ + "defaults to derive AUDIO settings [CamcorderProfile mime type: "
+ + camcorderProfileAudioMime + "(profile: " + camcorderProfileAudioProfile
+ + "), chosen mime type: "
+ + resolvedAudioMime + "(profile: " + resolvedAudioProfile + ")]");
+ }
+ }
+
+ MimeInfo.Builder mimeInfoBuilder = MimeInfo.builder(resolvedAudioMime)
+ .setProfile(resolvedAudioProfile);
+ if (camcorderProfileIsCompatible) {
+ mimeInfoBuilder.setCompatibleCamcorderProfile(mResolvedCamcorderProfile);
+ }
+
+ return mimeInfoBuilder.build();
}
@ExecutedBy("mSequentialExecutor")
@NonNull
- private VideoEncoderConfig composeVideoEncoderConfig(@NonNull MediaSpec mediaSpec,
- @NonNull Size surfaceSize) {
- return VideoEncoderConfig.builder()
- .setMimeType(MediaSpec.outputFormatToVideoMime(mediaSpec.getOutputFormat()))
- .setResolution(surfaceSize)
- // TODO: Add mechanism to pick a value from the specified range and
- // CamcorderProfile.
- .setBitrate(VIDEO_BITRATE_DEFAULT)
- .setFrameRate(VIDEO_FRAME_RATE_DEFAULT)
- .setColorFormat(MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface)
- .setIFrameInterval(VIDEO_INTRA_FRAME_INTERVAL_DEFAULT)
- .build();
+ private MimeInfo resolveVideoMimeInfo(@NonNull MediaSpec mediaSpec) {
+ String mediaSpecVideoMime = MediaSpec.outputFormatToVideoMime(mediaSpec.getOutputFormat());
+ String resolvedVideoMime = mediaSpecVideoMime;
+ boolean camcorderProfileIsCompatible = false;
+ if (mResolvedCamcorderProfile != null) {
+ String camcorderProfileVideoMime = mResolvedCamcorderProfile.getVideoCodecMimeType();
+ // Use camcorder profile settings if the media spec's output format
+ // is set to auto or happens to match the CamcorderProfile's output format.
+ if (camcorderProfileVideoMime == null) {
+ Logger.d(TAG, "CamcorderProfile contains undefined VIDEO mime type so cannot be "
+ + "used. May rely on fallback defaults to derive settings [chosen mime "
+ + "type: " + resolvedVideoMime + "]");
+ } else if (mediaSpec.getOutputFormat() == MediaSpec.OUTPUT_FORMAT_AUTO) {
+ camcorderProfileIsCompatible = true;
+ resolvedVideoMime = camcorderProfileVideoMime;
+ Logger.d(TAG, "MediaSpec contains OUTPUT_FORMAT_AUTO. Using CamcorderProfile "
+ + "to derive VIDEO settings [mime type: " + resolvedVideoMime + "]");
+ } else if (Objects.equals(mediaSpecVideoMime, camcorderProfileVideoMime)) {
+ camcorderProfileIsCompatible = true;
+ resolvedVideoMime = camcorderProfileVideoMime;
+ Logger.d(TAG, "MediaSpec video mime matches CamcorderProfile. Using "
+ + "CamcorderProfile to derive VIDEO settings [mime type: "
+ + resolvedVideoMime + "]");
+ } else {
+ Logger.d(TAG, "MediaSpec video mime does not match CamcorderProfile, so "
+ + "CamcorderProfile settings cannot be used. May rely on fallback "
+ + "defaults to derive VIDEO settings [CamcorderProfile mime type: "
+ + camcorderProfileVideoMime + ", chosen mime type: "
+ + resolvedVideoMime + "]");
+ }
+ } else {
+ Logger.d(TAG,
+ "No CamcorderProfile present. May rely on fallback defaults to derive VIDEO "
+ + "settings [chosen mime type: " + resolvedVideoMime + "]");
+ }
+
+ MimeInfo.Builder mimeInfoBuilder = MimeInfo.builder(resolvedVideoMime);
+ if (camcorderProfileIsCompatible) {
+ mimeInfoBuilder.setCompatibleCamcorderProfile(mResolvedCamcorderProfile);
+ }
+
+ return mimeInfoBuilder.build();
+ }
+
+ @NonNull
+ private static AudioSource.Settings resolveAudioSourceSettings(@NonNull MimeInfo audioMimeInfo,
+ @NonNull AudioSpec audioSpec) {
+ Supplier<AudioSource.Settings> settingsSupplier;
+ if (audioMimeInfo.getCompatibleCamcorderProfile() != null) {
+ settingsSupplier = new AudioSourceSettingsCamcorderProfileResolver(audioSpec,
+ audioMimeInfo.getCompatibleCamcorderProfile());
+ } else {
+ settingsSupplier = new AudioSourceSettingsDefaultResolver(audioSpec);
+ }
+
+ return settingsSupplier.get();
+ }
+
+ @NonNull
+ private static AudioEncoderConfig resolveAudioEncoderConfig(@NonNull MimeInfo audioMimeInfo,
+ @NonNull AudioSource.Settings audioSourceSettings, @NonNull AudioSpec audioSpec) {
+ Supplier<AudioEncoderConfig> configSupplier;
+ if (audioMimeInfo.getCompatibleCamcorderProfile() != null) {
+ configSupplier = new AudioEncoderConfigCamcorderProfileResolver(
+ audioMimeInfo.getMimeType(), audioMimeInfo.getProfile(), audioSpec,
+ audioSourceSettings, audioMimeInfo.getCompatibleCamcorderProfile());
+ } else {
+ configSupplier = new AudioEncoderConfigDefaultResolver(audioMimeInfo.getMimeType(),
+ audioMimeInfo.getProfile(), audioSpec, audioSourceSettings);
+ }
+
+ return configSupplier.get();
+ }
+
+ @NonNull
+ private static VideoEncoderConfig resolveVideoEncoderConfig(@NonNull MimeInfo videoMimeInfo,
+ @NonNull VideoSpec videoSpec, @NonNull Size surfaceSize) {
+ Supplier<VideoEncoderConfig> configSupplier;
+ if (videoMimeInfo.getCompatibleCamcorderProfile() != null) {
+ configSupplier = new VideoEncoderConfigCamcorderProfileResolver(
+ videoMimeInfo.getMimeType(), videoSpec, surfaceSize,
+ videoMimeInfo.getCompatibleCamcorderProfile());
+ } else {
+ configSupplier = new VideoEncoderConfigDefaultResolver(videoMimeInfo.getMimeType(),
+ videoSpec, surfaceSize);
+ }
+
+ return configSupplier.get();
}
/**
@@ -1103,38 +1202,41 @@
@ExecutedBy("mSequentialExecutor")
private void setupAudio() throws ResourceCreationException {
MediaSpec mediaSpec = getObservableData(mMediaSpec);
- AudioEncoderConfig config = composeAudioEncoderConfig(mediaSpec);
+ // Resolve the audio mime info
+ MimeInfo audioMimeInfo = resolveAudioMimeInfo(mediaSpec);
+ // Select and create the audio source
+ AudioSource.Settings audioSourceSettings =
+ resolveAudioSourceSettings(audioMimeInfo, mediaSpec.getAudioSpec());
try {
- mAudioEncoder = new EncoderImpl(mExecutor, config);
+ mAudioSource = setupAudioSource(audioSourceSettings);
+ } catch (AudioSourceAccessException e) {
+ throw new ResourceCreationException(e);
+ }
+
+ // Select and create the audio encoder
+ AudioEncoderConfig audioEncoderConfig = resolveAudioEncoderConfig(audioMimeInfo,
+ audioSourceSettings, mediaSpec.getAudioSpec());
+ try {
+ mAudioEncoder = new EncoderImpl(mExecutor, audioEncoderConfig);
} catch (InvalidConfigException e) {
throw new ResourceCreationException(e);
}
+ // Connect the audio source to the audio encoder
Encoder.EncoderInput bufferProvider = mAudioEncoder.getInput();
if (!(bufferProvider instanceof Encoder.ByteBufferInput)) {
throw new AssertionError("The EncoderInput of audio isn't a ByteBufferInput.");
}
- try {
- mAudioSource = setupAudioSource((Encoder.ByteBufferInput) bufferProvider,
- mediaSpec.getAudioSpec());
- } catch (AudioSourceAccessException e) {
- throw new ResourceCreationException(e);
- }
+ mAudioSource.setBufferProvider((Encoder.ByteBufferInput) bufferProvider);
}
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
@NonNull
- private AudioSource setupAudioSource(@NonNull BufferProvider<InputBuffer> bufferProvider,
- @NonNull AudioSpec audioSpec) throws AudioSourceAccessException {
- AudioSource audioSource = new AudioSource.Builder()
- .setExecutor(CameraXExecutors.ioExecutor())
- .setBufferProvider(bufferProvider)
- .setAudioSource(audioSpec.getSource())
- .setSampleRate(selectSampleRate(audioSpec))
- .setChannelCount(audioSpec.getChannelCount())
- .setAudioFormat(audioSpec.getSourceFormat())
- .build();
+ private AudioSource setupAudioSource(@NonNull AudioSource.Settings audioSourceSettings)
+ throws AudioSourceAccessException {
+ AudioSource audioSource = new AudioSource(audioSourceSettings,
+ CameraXExecutors.ioExecutor());
audioSource.setAudioSourceCallback(mSequentialExecutor,
new AudioSource.AudioSourceCallback() {
@Override
@@ -1162,30 +1264,11 @@
}
@ExecutedBy("mSequentialExecutor")
- private int selectSampleRate(AudioSpec audioSpec) {
- // The default sample rate should work on most devices. May consider throw an
- // exception or have other way to notify users that the specified sample rate
- // can not be satisfied.
- int selectedSampleRate = AUDIO_SAMPLE_RATE_DEFAULT;
- for (int sampleRate : AudioSource.COMMON_SAMPLE_RATES) {
- if (audioSpec.getSampleRate().contains(sampleRate)) {
- if (AudioSource.isSettingsSupported(sampleRate, audioSpec.getChannelCount(),
- audioSpec.getSourceFormat())) {
- // Choose the largest valid sample rate as the list has descending order.
- selectedSampleRate = sampleRate;
- break;
- }
- }
- }
-
- return selectedSampleRate;
- }
-
- @ExecutedBy("mSequentialExecutor")
private void setupVideo(@NonNull SurfaceRequest surfaceRequest) {
MediaSpec mediaSpec = getObservableData(mMediaSpec);
- VideoEncoderConfig config = composeVideoEncoderConfig(mediaSpec,
- surfaceRequest.getResolution());
+ MimeInfo videoMimeInfo = resolveVideoMimeInfo(mediaSpec);
+ VideoEncoderConfig config = resolveVideoEncoderConfig(videoMimeInfo,
+ mediaSpec.getVideoSpec(), surfaceRequest.getResolution());
try {
mVideoEncoder = new EncoderImpl(mExecutor, config);
@@ -1293,8 +1376,13 @@
}
try {
- int muxerOutputFormat = MediaSpec.outputFormatToMuxerFormat(
- getObservableData(mMediaSpec).getOutputFormat());
+ MediaSpec mediaSpec = getObservableData(mMediaSpec);
+ int muxerOutputFormat =
+ mediaSpec.getOutputFormat() == MediaSpec.OUTPUT_FORMAT_AUTO
+ ? supportedMuxerFormatOrDefaultFrom(mResolvedCamcorderProfile,
+ MediaSpec.outputFormatToMuxerFormat(
+ MEDIA_SPEC_DEFAULT.getOutputFormat()))
+ : MediaSpec.outputFormatToMuxerFormat(mediaSpec.getOutputFormat());
mMediaMuxer = recordingToStart.performOneTimeMediaMuxerCreation(muxerOutputFormat,
uri -> mOutputUri = uri);
} catch (IOException e) {
@@ -2143,6 +2231,23 @@
mAudioState = audioState;
}
+ private static int supportedMuxerFormatOrDefaultFrom(
+ @Nullable CamcorderProfileProxy profileProxy, int defaultMuxerFormat) {
+ if (profileProxy != null) {
+ switch (profileProxy.getFileFormat()) {
+ case MediaRecorder.OutputFormat.MPEG_4:
+ return MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
+ case MediaRecorder.OutputFormat.WEBM:
+ return MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM;
+ case MediaRecorder.OutputFormat.THREE_GPP:
+ return MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP;
+ default:
+ break;
+ }
+ }
+ return defaultMuxerFormat;
+ }
+
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
@AutoValue
abstract static class RecordingRecord implements AutoCloseable {
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapabilities.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapabilities.java
index 65626a4..4ed6cdd 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapabilities.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapabilities.java
@@ -16,6 +16,8 @@
package androidx.camera.video;
+import android.util.Size;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@@ -26,6 +28,7 @@
import androidx.camera.core.impl.CamcorderProfileProvider;
import androidx.camera.core.impl.CamcorderProfileProxy;
import androidx.camera.core.impl.CameraInfoInternal;
+import androidx.camera.core.impl.utils.CompareSizesByArea;
import androidx.camera.video.internal.compat.quirk.DeviceQuirks;
import androidx.camera.video.internal.compat.quirk.VideoQualityNotSupportQuirk;
import androidx.core.util.Preconditions;
@@ -36,6 +39,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
/**
* VideoCapabilities is used to query video recording capabilities on the device.
@@ -53,7 +57,9 @@
* The supported @link CamcorderProfileProxy} map from quality to CamcorderProfileProxy. The
* order is from size large to small.
*/
- private final Map<Integer, CamcorderProfileProxy> mSupportedProfileMap = new LinkedHashMap<>();
+ private final Map<Quality, CamcorderProfileProxy> mSupportedProfileMap = new LinkedHashMap<>();
+ private final TreeMap<Size, Quality> mAreaSortedSizeToQualityMap =
+ new TreeMap<>(new CompareSizesByArea());
private final CamcorderProfileProxy mHighestProfile;
private final CamcorderProfileProxy mLowestProfile;
@@ -69,16 +75,24 @@
cameraInfoInternal.getCamcorderProfileProvider();
// Construct supported profile map
- for (@QualitySelector.VideoQuality int quality : QualitySelector.getSortedQualities()) {
+ for (Quality quality : Quality.getSortedQualities()) {
// SortedQualities is from size large to small
+ Preconditions.checkState(quality instanceof Quality.ConstantQuality,
+ "Currently only support ConstantQuality");
+ int qualityValue = ((Quality.ConstantQuality) quality).getValue();
// Get CamcorderProfile
- if (!camcorderProfileProvider.hasProfile(quality) || !isDeviceValidQuality(quality)) {
+ if (!camcorderProfileProvider.hasProfile(qualityValue) || !isDeviceValidQuality(
+ quality)) {
continue;
}
- CamcorderProfileProxy profile = camcorderProfileProvider.get(quality);
+ CamcorderProfileProxy profile =
+ Preconditions.checkNotNull(camcorderProfileProvider.get(qualityValue));
+ Size profileSize = new Size(profile.getVideoFrameWidth(),
+ profile.getVideoFrameHeight());
Logger.d(TAG, "profile = " + profile);
mSupportedProfileMap.put(quality, profile);
+ mAreaSortedSizeToQualityMap.put(profileSize, quality);
}
if (mSupportedProfileMap.isEmpty()) {
Logger.e(TAG, "No supported CamcorderProfile");
@@ -102,14 +116,13 @@
* Gets all supported qualities on the device.
*
* <p>The returned list is sorted by quality size from large to small. For the qualities in
- * the returned list, calling {@link #getProfile(int)} with these qualities will return a
+ * the returned list, calling {@link #getProfile(Quality)} with these qualities will return a
* non-null result.
*
- * <p>Note: Constants {@link QualitySelector#QUALITY_HIGHEST} and
- * {@link QualitySelector#QUALITY_LOWEST} are not included.
+ * <p>Note: Constants {@link Quality#HIGHEST} and {@link Quality#LOWEST} are not included.
*/
@NonNull
- public List<Integer> getSupportedQualities() {
+ public List<Quality> getSupportedQualities() {
return new ArrayList<>(mSupportedProfileMap.keySet());
}
@@ -117,13 +130,12 @@
* Checks if the quality is supported.
*
* @param quality one of the quality constants. Possible values include
- * {@link QualitySelector#QUALITY_LOWEST}, {@link QualitySelector#QUALITY_HIGHEST},
- * {@link QualitySelector#QUALITY_SD}, {@link QualitySelector#QUALITY_HD},
- * {@link QualitySelector#QUALITY_FHD}, or {@link QualitySelector#QUALITY_UHD}.
+ * {@link Quality#LOWEST}, {@link Quality#HIGHEST}, {@link {@link Quality#SD},
+ * {@link Quality#HD}, {@link {@link Quality#FHD}, or {@link Quality#UHD}.
* @return {@code true} if the quality is supported; {@code false} otherwise.
* @throws IllegalArgumentException if not a quality constant.
*/
- public boolean isQualitySupported(@QualitySelector.VideoQuality int quality) {
+ public boolean isQualitySupported(@NonNull Quality quality) {
checkQualityConstantsOrThrow(quality);
return getProfile(quality) != null;
}
@@ -132,29 +144,60 @@
* Gets the corresponding {@link CamcorderProfileProxy} of the input quality.
*
* @param quality one of the quality constants. Possible values include
- * {@link QualitySelector#QUALITY_LOWEST}, {@link QualitySelector#QUALITY_HIGHEST},
- * {@link QualitySelector#QUALITY_SD}, {@link QualitySelector#QUALITY_HD},
- * {@link QualitySelector#QUALITY_FHD}, or {@link QualitySelector#QUALITY_UHD}.
+ * {@link Quality#LOWEST}, {@link Quality#HIGHEST}, {@link Quality#SD}, {@link Quality#HD},
+ * {@link Quality#FHD}, or {@link Quality#UHD}.
* @return the CamcorderProfileProxy
* @throws IllegalArgumentException if not a quality constant
*/
@Nullable
- public CamcorderProfileProxy getProfile(@QualitySelector.VideoQuality int quality) {
+ public CamcorderProfileProxy getProfile(@NonNull Quality quality) {
checkQualityConstantsOrThrow(quality);
- if (quality == QualitySelector.QUALITY_HIGHEST) {
+ if (quality == Quality.HIGHEST) {
return mHighestProfile;
- } else if (quality == QualitySelector.QUALITY_LOWEST) {
+ } else if (quality == Quality.LOWEST) {
return mLowestProfile;
}
return mSupportedProfileMap.get(quality);
}
- private static void checkQualityConstantsOrThrow(@QualitySelector.VideoQuality int quality) {
- Preconditions.checkArgument(QualitySelector.containsQuality(quality),
+ /**
+ * Finds the nearest quality by number of pixels to the given {@link Size}.
+ *
+ * <p>If the size aligns exactly with the pixel count of a supported quality, that quality
+ * will be selected. If the size falls between two qualities, the higher quality will always
+ * be selected. Otherwise, the nearest single quality will be selected, whether that
+ * quality's size is above or below the given size.
+ * @param size The size representing the number of pixels for comparison. Pixels are assumed
+ * to be square.
+ * @return The quality constant defined in {@link Quality}. If no qualities are supported,
+ * then {@link Quality#NONE} is returned.
+ */
+ @NonNull
+ public Quality findHighestSupportedQualityFor(@NonNull Size size) {
+ Map.Entry<Size, Quality> ceilEntry = mAreaSortedSizeToQualityMap.ceilingEntry(size);
+
+ if (ceilEntry != null) {
+ // The ceiling entry will either be equivalent or higher in size, so always return it.
+ return ceilEntry.getValue();
+ } else {
+ // If a ceiling entry doesn't exist and a floor entry exists, it is the closest we have,
+ // so return it.
+ Map.Entry<Size, Quality> floorEntry = mAreaSortedSizeToQualityMap.floorEntry(size);
+ if (floorEntry != null) {
+ return floorEntry.getValue();
+ }
+ }
+
+ // No supported qualities.
+ return Quality.NONE;
+ }
+
+ private static void checkQualityConstantsOrThrow(@NonNull Quality quality) {
+ Preconditions.checkArgument(Quality.containsQuality(quality),
"Unknown quality: " + quality);
}
- private boolean isDeviceValidQuality(@QualitySelector.VideoQuality int quality) {
+ private boolean isDeviceValidQuality(@NonNull Quality quality) {
VideoQualityNotSupportQuirk quirk = DeviceQuirks.get(VideoQualityNotSupportQuirk.class);
return quirk == null || !quirk.isProblematicVideoQuality(quality);
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
index 09c594c..08d13bf 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/VideoCapture.java
@@ -511,10 +511,10 @@
Preconditions.checkArgument(mediaSpec != null,
"Unable to update target resolution by null MediaSpec.");
- List<Integer> supportedQualities = QualitySelector.getSupportedQualities(cameraInfo);
+ List<Quality> supportedQualities = QualitySelector.getSupportedQualities(cameraInfo);
if (supportedQualities.isEmpty()) {
// When the device does not have any supported quality, even the most flexible
- // QualitySelector such as QualitySelector.of(QUALITY_HIGHEST), still cannot
+ // QualitySelector such as QualitySelector.from(Quality.HIGHEST), still cannot
// find any resolution. This should be a rare case but will cause VideoCapture
// to always fail to bind. The workaround is not set any resolution and leave it to
// auto resolution mechanism.
@@ -524,7 +524,7 @@
QualitySelector qualitySelector = mediaSpec.getVideoSpec().getQualitySelector();
- List<Integer> selectedQualities = qualitySelector.getPrioritizedQualities(cameraInfo);
+ List<Quality> selectedQualities = qualitySelector.getPrioritizedQualities(cameraInfo);
Logger.d(TAG,
"Found selectedQualities " + selectedQualities + " by " + qualitySelector);
@@ -535,7 +535,7 @@
}
List<Size> supportedResolutions = new ArrayList<>();
- for (Integer selectedQuality : selectedQualities) {
+ for (Quality selectedQuality : selectedQualities) {
supportedResolutions.add(QualitySelector.getResolution(cameraInfo, selectedQuality));
}
Logger.d(TAG, "Set supported resolutions = " + supportedResolutions);
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/VideoSpec.java b/camera/camera-video/src/main/java/androidx/camera/video/VideoSpec.java
index a1efbe28..94f15ac 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/VideoSpec.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/VideoSpec.java
@@ -16,11 +16,6 @@
package androidx.camera.video;
-import static androidx.camera.video.QualitySelector.FALLBACK_STRATEGY_HIGHER;
-import static androidx.camera.video.QualitySelector.QUALITY_FHD;
-import static androidx.camera.video.QualitySelector.QUALITY_HD;
-import static androidx.camera.video.QualitySelector.QUALITY_SD;
-
import android.util.Range;
import androidx.annotation.IntDef;
@@ -33,6 +28,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
/**
* Video specification that is options to config video encoding.
@@ -71,10 +67,8 @@
*/
@NonNull
public static final QualitySelector QUALITY_SELECTOR_AUTO =
- QualitySelector.firstTry(QUALITY_FHD)
- .thenTry(QUALITY_HD)
- .thenTry(QUALITY_SD)
- .finallyTry(QUALITY_FHD, FALLBACK_STRATEGY_HIGHER);
+ QualitySelector.fromOrderedList(Arrays.asList(Quality.FHD, Quality.HD, Quality.SD),
+ FallbackStrategy.higherQualityOrLowerThan(Quality.FHD));
/**
* The aspect ratio representing no preference for aspect ratio.
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java
index 6a6dd23..99d899c 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/AudioSource.java
@@ -21,6 +21,7 @@
import static androidx.camera.video.internal.AudioSource.InternalState.STARTED;
import android.Manifest;
+import android.annotation.SuppressLint;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
@@ -28,6 +29,7 @@
import android.media.AudioTimestamp;
import android.os.Build;
+import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@@ -43,6 +45,8 @@
import androidx.camera.video.internal.encoder.InputBuffer;
import androidx.core.util.Preconditions;
+import com.google.auto.value.AutoValue;
+
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
@@ -55,7 +59,7 @@
* AudioSource is used to obtain audio raw data and write to the buffer from {@link BufferProvider}.
*
* <p>The audio raw data could be one of sources from the device. The target source can be
- * specified with {@link Builder#setAudioSource(int)}.
+ * specified with {@link Settings.Builder#setAudioSource(int)}.
*
* <p>Calling {@link #start} will start reading audio data from the target source and then write
* the data into the buffer from {@link BufferProvider}. Calling {@link #stop} will stop sending
@@ -87,8 +91,6 @@
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
final Executor mExecutor;
- private final BufferProvider<InputBuffer> mBufferProvider;
-
private AudioManager.AudioRecordingCallback mAudioRecordingCallback;
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
@@ -115,28 +117,50 @@
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
AudioSourceCallback mAudioSourceCallback;
+ // The following should only be accessed by mExecutor
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
+ BufferProvider<InputBuffer> mBufferProvider;
+ private FutureCallback<InputBuffer> mAcquireBufferCallback;
+ private Observable.Observer<BufferProvider.State> mStateObserver;
+
+
+ /**
+ * Creates an AudioSource for the given settings.
+ *
+ * <p>It should be verified the combination of sample rate, channel count and audio format is
+ * supported with {@link #isSettingsSupported(int, int, int)} before passing the settings to
+ * this constructor, or an {@link UnsupportedOperationException} will be thrown.
+ *
+ * @throws UnsupportedOperationException if the combination of sample rate, channel count,
+ * and audio format in the provided settings is unsupported.
+ * @throws AudioSourceAccessException if the audio device is not available or cannot be
+ * initialized with the given settings.
+ */
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
- AudioSource(@NonNull Executor executor,
- @NonNull BufferProvider<InputBuffer> bufferProvider,
- int audioSource,
- int sampleRate,
- int channelCount,
- int audioFormat)
+ public AudioSource(@NonNull Settings settings, @NonNull Executor executor)
throws AudioSourceAccessException {
- int minBufferSize = getMinBufferSize(sampleRate, channelCount, audioFormat);
+ if (!isSettingsSupported(settings.getSampleRate(), settings.getChannelCount(),
+ settings.getAudioFormat())) {
+ throw new UnsupportedOperationException(String.format(
+ "The combination of sample rate %d, channel count %d and audio format"
+ + " %d is not supported.",
+ settings.getSampleRate(), settings.getChannelCount(),
+ settings.getAudioFormat()));
+ }
+
+ int minBufferSize = getMinBufferSize(settings.getSampleRate(), settings.getChannelCount(),
+ settings.getAudioFormat());
// The minBufferSize should be a positive value since the settings had already been checked
// by the isSettingsSupported().
Preconditions.checkState(minBufferSize > 0);
mExecutor = CameraXExecutors.newSequentialExecutor(executor);
- mBufferProvider = bufferProvider;
mBufferSize = minBufferSize * 2;
try {
- mAudioRecord = new AudioRecord(audioSource,
- sampleRate,
- channelCountToChannelConfig(channelCount),
- audioFormat,
+ mAudioRecord = new AudioRecord(settings.getAudioSource(),
+ settings.getSampleRate(),
+ channelCountToChannelConfig(settings.getChannelCount()),
+ settings.getAudioFormat(),
mBufferSize);
} catch (IllegalArgumentException e) {
throw new AudioSourceAccessException("Unable to create AudioRecord", e);
@@ -152,8 +176,6 @@
Api29Impl.registerAudioRecordingCallback(mAudioRecord, mExecutor,
mAudioRecordingCallback);
}
-
- mBufferProvider.addObserver(mExecutor, mStateObserver);
}
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
@@ -178,9 +200,38 @@
}
}
+
+ /**
+ * Sets the {@link BufferProvider}.
+ *
+ * <p>A buffer provider is required to stream audio. If no buffer provider is provided, then
+ * audio will be dropped until one is provided and active.
+ *
+ * @param bufferProvider The new buffer provider to use.
+ */
+ public void setBufferProvider(@NonNull BufferProvider<InputBuffer> bufferProvider) {
+ mExecutor.execute(() -> {
+ switch (mState) {
+ case CONFIGURED:
+ // Fall-through
+ case STARTED:
+ if (mBufferProvider != bufferProvider) {
+ resetBufferProvider(bufferProvider);
+ }
+ break;
+ case RELEASED:
+ throw new IllegalStateException("AudioRecorder is released");
+ }
+ });
+ }
+
/**
* Starts the AudioSource.
*
+ * <p>Before starting, a {@link BufferProvider} should be set with
+ * {@link #setBufferProvider(BufferProvider)}. If a buffer provider is not set, audio data
+ * will be dropped.
+ *
* <p>Audio data will start being sent to the {@link BufferProvider} when
* {@link BufferProvider}'s state is {@link BufferProvider.State#ACTIVE}.
*
@@ -236,7 +287,7 @@
case STARTED:
// Fall-through
case CONFIGURED:
- mBufferProvider.removeObserver(mStateObserver);
+ resetBufferProvider(null);
if (Build.VERSION.SDK_INT >= 29) {
Api29Impl.unregisterAudioRecordingCallback(mAudioRecord,
mAudioRecordingCallback);
@@ -277,6 +328,75 @@
});
}
+ @ExecutedBy("mExecutor")
+ private void resetBufferProvider(@Nullable BufferProvider<InputBuffer> bufferProvider) {
+ if (mBufferProvider != null) {
+ mBufferProvider.removeObserver(mStateObserver);
+ mBufferProvider = null;
+ mStateObserver = null;
+ mAcquireBufferCallback = null;
+ }
+ mBufferProviderState = BufferProvider.State.INACTIVE;
+ updateSendingAudio();
+ if (bufferProvider != null) {
+ mBufferProvider = bufferProvider;
+ mStateObserver = new Observable.Observer<BufferProvider.State>() {
+ @ExecutedBy("mExecutor")
+ @Override
+ public void onNewData(@Nullable BufferProvider.State state) {
+ if (mBufferProvider == bufferProvider) {
+ Logger.d(TAG, "Receive BufferProvider state change: "
+ + mBufferProviderState + " to " + state);
+ mBufferProviderState = state;
+ updateSendingAudio();
+ }
+ }
+
+ @ExecutedBy("mExecutor")
+ @Override
+ public void onError(@NonNull Throwable throwable) {
+ if (mBufferProvider == bufferProvider) {
+ notifyError(throwable);
+ }
+ }
+ };
+
+ mAcquireBufferCallback = new FutureCallback<InputBuffer>() {
+ @ExecutedBy("mExecutor")
+ @Override
+ public void onSuccess(InputBuffer inputBuffer) {
+ if (!mIsSendingAudio || mBufferProvider != bufferProvider) {
+ inputBuffer.cancel();
+ return;
+ }
+ ByteBuffer byteBuffer = inputBuffer.getByteBuffer();
+
+ int length = mAudioRecord.read(byteBuffer, mBufferSize);
+ if (length > 0) {
+ byteBuffer.limit(length);
+ inputBuffer.setPresentationTimeUs(generatePresentationTimeUs());
+ inputBuffer.submit();
+ } else {
+ Logger.w(TAG, "Unable to read data from AudioRecord.");
+ inputBuffer.cancel();
+ }
+ sendNextAudio();
+ }
+
+ @ExecutedBy("mExecutor")
+ @Override
+ public void onFailure(Throwable throwable) {
+ if (mBufferProvider != bufferProvider) {
+ Logger.d(TAG, "Unable to get input buffer, the BufferProvider "
+ + "could be transitioning to INACTIVE state.");
+ notifyError(throwable);
+ }
+ }
+ };
+ mBufferProvider.addObserver(mExecutor, mStateObserver);
+ }
+ }
+
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
void notifyError(Throwable throwable) {
if (mCallbackExecutor != null && mAudioSourceCallback != null) {
@@ -368,56 +488,6 @@
return presentationTimeUs;
}
- private final FutureCallback<InputBuffer> mAcquireBufferCallback =
- new FutureCallback<InputBuffer>() {
- @ExecutedBy("mExecutor")
- @Override
- public void onSuccess(InputBuffer inputBuffer) {
- if (!mIsSendingAudio) {
- inputBuffer.cancel();
- return;
- }
- ByteBuffer byteBuffer = inputBuffer.getByteBuffer();
-
- int length = mAudioRecord.read(byteBuffer, mBufferSize);
- if (length > 0) {
- byteBuffer.limit(length);
- inputBuffer.setPresentationTimeUs(generatePresentationTimeUs());
- inputBuffer.submit();
- } else {
- Logger.w(TAG, "Unable to read data from AudioRecord.");
- inputBuffer.cancel();
- }
- sendNextAudio();
- }
-
- @ExecutedBy("mExecutor")
- @Override
- public void onFailure(Throwable throwable) {
- Logger.d(TAG, "Unable to get input buffer, the BufferProvider "
- + "could be transitioning to INACTIVE state.");
- notifyError(throwable);
- }
- };
-
- private final Observable.Observer<BufferProvider.State> mStateObserver =
- new Observable.Observer<BufferProvider.State>() {
- @ExecutedBy("mExecutor")
- @Override
- public void onNewData(@Nullable BufferProvider.State state) {
- Logger.d(TAG, "Receive BufferProvider state change: "
- + mBufferProviderState + " to " + state);
- mBufferProviderState = state;
- updateSendingAudio();
- }
-
- @ExecutedBy("mExecutor")
- @Override
- public void onError(@NonNull Throwable throwable) {
- notifyError(throwable);
- }
- };
-
/** Check if the combination of sample rate, channel count and audio format is supported. */
public static boolean isSettingsSupported(int sampleRate, int channelCount, int audioFormat) {
if (sampleRate <= 0 || channelCount <= 0) {
@@ -436,124 +506,133 @@
}
/**
- * The builder of the AudioSource.
+ * Settings required to configure the audio source.
*/
- public static class Builder {
- private Executor mExecutor;
- private int mAudioSource = -1;
- private int mSampleRate = -1;
- private int mChannelCount = -1;
- private int mAudioFormat = -1;
- private BufferProvider<InputBuffer> mBufferProvider;
+ @AutoValue
+ public abstract static class Settings {
- /** Sets the executor to run the background task. */
+ /** Creates a builder for these settings. */
+ @SuppressLint("Range") // Need to initialize as invalid values
@NonNull
- public Builder setExecutor(@NonNull Executor executor) {
- mExecutor = Preconditions.checkNotNull(executor);
- return this;
+ public static Settings.Builder builder() {
+ return new AutoValue_AudioSource_Settings.Builder()
+ .setAudioSource(-1)
+ .setSampleRate(-1)
+ .setChannelCount(-1)
+ .setAudioFormat(-1);
}
+ /** Creates a {@link Builder} initialized with the same settings as this instance. */
+ @NonNull
+ public abstract Builder toBuilder();
+
/**
- * Sets the device audio source.
+ * Gets the device audio source.
*
* @see android.media.MediaRecorder.AudioSource#MIC
* @see android.media.MediaRecorder.AudioSource#CAMCORDER
*/
- @NonNull
- public Builder setAudioSource(int audioSource) {
- mAudioSource = audioSource;
- return this;
- }
+ public abstract int getAudioSource();
/**
- * Sets the audio sample rate.
- *
- * <p>It has to ensure the combination of sample rate, channel count and audio format is
- * supported by {@link AudioSource#isSettingsSupported(int, int, int)}.
- *
- * @throws IllegalArgumentException if the sample rate is not positive.
+ * Gets the audio sample rate.
*/
- @NonNull
- public Builder setSampleRate(int sampleRate) {
- Preconditions.checkArgument(sampleRate > 0);
- mSampleRate = sampleRate;
- return this;
- }
+ @IntRange(from = 1)
+ public abstract int getSampleRate();
/**
- * Sets the channel count.
- *
- * <p>It has to ensure the combination of sample rate, channel count and audio format is
- * supported by {@link AudioSource#isSettingsSupported(int, int, int)}.
- *
- * @throws IllegalArgumentException if the channel count is not positive.
+ * Gets the channel count.
*/
- @NonNull
- public Builder setChannelCount(int channelCount) {
- Preconditions.checkArgument(channelCount > 0);
- mChannelCount = channelCount;
- return this;
- }
+ @IntRange(from = 1)
+ public abstract int getChannelCount();
/**
* Sets the audio format.
*
- * <p>It has to ensure the combination of sample rate, channel count and audio format is
- * supported by {@link AudioSource#isSettingsSupported(int, int, int)}.
- *
* @see AudioFormat#ENCODING_PCM_16BIT
*/
- @NonNull
- public Builder setAudioFormat(int audioFormat) {
- mAudioFormat = audioFormat;
- return this;
+ public abstract int getAudioFormat();
+
+ // Should not be instantiated directly
+ Settings() {
}
- /** Sets the {@link BufferProvider}. */
- @NonNull
- public Builder setBufferProvider(@NonNull BufferProvider<InputBuffer> bufferProvider) {
- mBufferProvider = Preconditions.checkNotNull(bufferProvider);
- return this;
- }
+ /**
+ * A Builder for {@link AudioSource.Settings}
+ */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ /**
+ * Sets the device audio source.
+ *
+ * @see android.media.MediaRecorder.AudioSource#MIC
+ * @see android.media.MediaRecorder.AudioSource#CAMCORDER
+ */
+ @NonNull
+ public abstract Builder setAudioSource(int audioSource);
- /** Build the AudioSource. */
- @RequiresPermission(Manifest.permission.RECORD_AUDIO)
- @NonNull
- public AudioSource build() throws AudioSourceAccessException {
- String missing = "";
- if (mExecutor == null) {
- missing += " executor";
+ /**
+ * Sets the audio sample rate in Hertz.
+ */
+ @NonNull
+ public abstract Builder setSampleRate(@IntRange(from = 1) int sampleRate);
+
+ /**
+ * Sets the channel count.
+ */
+ @NonNull
+ public abstract Builder setChannelCount(@IntRange(from = 1) int channelCount);
+
+ /**
+ * Sets the audio format.
+ *
+ * @see AudioFormat#ENCODING_PCM_16BIT
+ */
+ @NonNull
+ public abstract Builder setAudioFormat(int audioFormat);
+
+ abstract Settings autoBuild(); // Actual build method. Not public.
+
+ /**
+ * Returns the built config after performing settings validation.
+ *
+ * <p>It should be verified that combination of sample rate, channel count and audio
+ * format is supported by {@link AudioSource#isSettingsSupported(int, int, int)} or
+ * an {@link UnsupportedOperationException} will be thrown when passing the settings
+ * to the
+ * {@linkplain AudioSource#AudioSource(Settings, Executor) AudioSource
+ * constructor}.
+ *
+ * @throws IllegalArgumentException if a setting is missing or invalid.
+ */
+ @NonNull
+ public final Settings build() {
+ Settings settings = autoBuild();
+ String missingOrInvalid = "";
+ if (settings.getAudioSource() == -1) {
+ missingOrInvalid += " audioSource";
+ }
+ if (settings.getSampleRate() <= 0) {
+ missingOrInvalid += " sampleRate";
+ }
+ if (settings.getChannelCount() <= 0) {
+ missingOrInvalid += " channelCount";
+ }
+ if (settings.getAudioFormat() == -1) {
+ missingOrInvalid += " audioFormat";
+ }
+
+ if (!missingOrInvalid.isEmpty()) {
+ throw new IllegalArgumentException("Required settings missing or "
+ + "non-positive:" + missingOrInvalid);
+ }
+
+ return settings;
}
- if (mBufferProvider == null) {
- missing += " bufferProvider";
+
+ // Should not be instantiated directly
+ Builder() {
}
- if (mAudioSource == -1) {
- missing += " audioSource";
- }
- if (mSampleRate == -1) {
- missing += " sampleRate";
- }
- if (mChannelCount == -1) {
- missing += " channelCount";
- }
- if (mAudioFormat == -1) {
- missing += " audioFormat";
- }
- if (!missing.isEmpty()) {
- throw new IllegalStateException("Missing required properties:" + missing);
- }
- if (!isSettingsSupported(mSampleRate, mChannelCount, mAudioFormat)) {
- throw new IllegalStateException(String.format("The combination of sample rate %d "
- + ", channel count %d and audio format %d is not supported.",
- mSampleRate, mChannelCount, mAudioFormat));
- }
- return new AudioSource(mExecutor,
- mBufferProvider,
- mAudioSource,
- mSampleRate,
- mChannelCount,
- mAudioFormat
- );
}
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/VideoQualityNotSupportQuirk.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/VideoQualityNotSupportQuirk.java
index 5d461ba..a307aa0 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/VideoQualityNotSupportQuirk.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/compat/quirk/VideoQualityNotSupportQuirk.java
@@ -20,10 +20,11 @@
import android.media.MediaRecorder.VideoEncoder;
import android.os.Build;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.camera.core.impl.CamcorderProfileProvider;
import androidx.camera.core.impl.Quirk;
-import androidx.camera.video.QualitySelector;
+import androidx.camera.video.Quality;
import androidx.camera.video.VideoCapabilities;
/**
@@ -52,7 +53,7 @@
}
/** Checks if the given mime type is a problematic quality. */
- public boolean isProblematicVideoQuality(@QualitySelector.VideoQuality int quality) {
- return quality == QualitySelector.QUALITY_UHD;
+ public boolean isProblematicVideoQuality(@NonNull Quality quality) {
+ return quality == Quality.UHD;
}
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioConfigUtil.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioConfigUtil.java
new file mode 100644
index 0000000..f388b3b
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioConfigUtil.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config;
+
+import android.util.Range;
+import android.util.Rational;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
+import androidx.camera.video.AudioSpec;
+import androidx.camera.video.internal.AudioSource;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A collection of utilities used for resolving and debugging audio configurations.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public final class AudioConfigUtil {
+ private static final String TAG = "AudioConfigUtil";
+
+ // Default to 44100 for now as it's guaranteed supported on devices.
+ static final int AUDIO_SAMPLE_RATE_DEFAULT = 44100;
+ // Default to mono since that should be supported on the most devices.
+ static final int AUDIO_CHANNEL_COUNT_DEFAULT = AudioSpec.CHANNEL_COUNT_MONO;
+ // Defaults to PCM_16BIT as it's guaranteed supported on devices.
+ static final int AUDIO_SOURCE_FORMAT_DEFAULT = AudioSpec.SOURCE_FORMAT_PCM_16BIT;
+ // Defaults to Camcorder as this should be the source closest to the camera
+ static final int AUDIO_SOURCE_DEFAULT = AudioSpec.SOURCE_CAMCORDER;
+
+ // Should not be instantiated.
+ private AudioConfigUtil() {
+ }
+
+ static int resolveAudioSource(@NonNull AudioSpec audioSpec) {
+ int resolvedAudioSource = audioSpec.getSource();
+ if (resolvedAudioSource == AudioSpec.SOURCE_AUTO) {
+ resolvedAudioSource = AUDIO_SOURCE_DEFAULT;
+ Logger.d(TAG, "Using default AUDIO source: " + resolvedAudioSource);
+ } else {
+ Logger.d(TAG, "Using provided AUDIO source: " + resolvedAudioSource);
+ }
+
+ return resolvedAudioSource;
+ }
+
+ static int resolveAudioSourceFormat(@NonNull AudioSpec audioSpec) {
+ int resolvedAudioSourceFormat = audioSpec.getSourceFormat();
+ if (resolvedAudioSourceFormat == AudioSpec.SOURCE_FORMAT_AUTO) {
+ // TODO: This should come from a priority list and may need to be combined with
+ // AudioSource.isSettingsSupported.
+ resolvedAudioSourceFormat = AUDIO_SOURCE_FORMAT_DEFAULT;
+ Logger.d(TAG, "Using default AUDIO source format: " + resolvedAudioSourceFormat);
+ } else {
+ Logger.d(
+ TAG, "Using provided AUDIO source format: " + resolvedAudioSourceFormat);
+ }
+
+ return resolvedAudioSourceFormat;
+ }
+
+ static int selectSampleRateOrNearestSupported(@NonNull Range<Integer> targetRange,
+ int channelCount, int sourceFormat, int initialTargetSampleRate) {
+ int selectedSampleRate = initialTargetSampleRate;
+ // Sample rates sorted by proximity to initial target.
+ List<Integer> sortedCommonSampleRates = null;
+ int i = 0;
+ do {
+ if (targetRange.contains(selectedSampleRate)) {
+ if (AudioSource.isSettingsSupported(selectedSampleRate, channelCount,
+ sourceFormat)) {
+ return selectedSampleRate;
+ } else {
+ Logger.d(TAG, "Sample rate " + selectedSampleRate + "Hz is not supported by "
+ + "audio source with channel count " + channelCount + " and source "
+ + "format " + sourceFormat);
+ }
+ } else {
+ Logger.d(TAG, "Sample rate " + selectedSampleRate + "Hz is not in target range "
+ + targetRange);
+ }
+
+ // If the initial target isn't supported, sort the array of published common sample
+ // rates by closeness to target and step through until we've found one that is
+ // supported.
+ if (sortedCommonSampleRates == null) {
+ Logger.d(TAG,
+ "Trying common sample rates in proximity order to target "
+ + initialTargetSampleRate + "Hz");
+ sortedCommonSampleRates = new ArrayList<>(AudioSource.COMMON_SAMPLE_RATES);
+ Collections.sort(sortedCommonSampleRates, (x, y) -> {
+ int relativeDifference = Math.abs(x - initialTargetSampleRate) - Math.abs(
+ y - initialTargetSampleRate);
+ // If the relative difference is zero, i.e., the target is halfway
+ // between the two, always prefer the larger sample rate for quality.
+ if (relativeDifference == 0) {
+ return (int) Math.signum(x - y);
+ }
+
+ return (int) Math.signum(relativeDifference);
+ });
+ }
+
+ if (i < sortedCommonSampleRates.size()) {
+ selectedSampleRate = sortedCommonSampleRates.get(i++);
+ } else {
+ break;
+ }
+ } while (true);
+
+ // No supported sample rate found. The default sample rate should work on most devices. May
+ // consider throw an exception or have other way to notify users that the specified
+ // sample rate can not be satisfied.
+ Logger.d(TAG, "No sample rate found in target range or supported by audio source. Falling"
+ + " back to default sample rate of " + AUDIO_SAMPLE_RATE_DEFAULT + "Hz");
+ return AUDIO_SAMPLE_RATE_DEFAULT;
+ }
+
+ static int scaleAndClampBitrate(int baseBitrate,
+ int actualChannelCount, int baseChannelCount,
+ int actualSampleRate, int baseSampleRate,
+ Range<Integer> clampedRange) {
+ // Scale bitrate based on source number of channels relative to base channel count.
+ Rational channelCountRatio = new Rational(actualChannelCount, baseChannelCount);
+ // Scale bitrate based on source sample rate relative to profile sample rate.
+ Rational sampleRateRatio = new Rational(actualSampleRate, baseSampleRate);
+
+ int resolvedBitrate = (int) (baseBitrate * channelCountRatio.doubleValue()
+ * sampleRateRatio.doubleValue());
+
+ String debugString = "";
+ if (Logger.isDebugEnabled(TAG)) {
+ debugString = String.format("Base Bitrate(%dbps) * Channel Count Ratio(%d / %d) * "
+ + "Sample Rate Ratio(%d / %d) = %d", baseBitrate, actualChannelCount,
+ baseChannelCount, actualSampleRate, baseSampleRate, resolvedBitrate);
+ }
+
+ if (!AudioSpec.BITRATE_RANGE_AUTO.equals(clampedRange)) {
+ resolvedBitrate = clampedRange.clamp(resolvedBitrate);
+ if (Logger.isDebugEnabled(TAG)) {
+ debugString += String.format("\nClamped to range %s -> %dbps", clampedRange,
+ resolvedBitrate);
+ }
+ }
+ Logger.d(TAG, debugString);
+ return resolvedBitrate;
+ }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioEncoderConfigCamcorderProfileResolver.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioEncoderConfigCamcorderProfileResolver.java
new file mode 100644
index 0000000..381510b5
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioEncoderConfigCamcorderProfileResolver.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config;
+
+
+import android.util.Range;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
+import androidx.camera.core.impl.CamcorderProfileProxy;
+import androidx.camera.video.AudioSpec;
+import androidx.camera.video.internal.AudioSource;
+import androidx.camera.video.internal.encoder.AudioEncoderConfig;
+import androidx.core.util.Supplier;
+
+/**
+ * An {@link AudioEncoderConfig} supplier that resolves requested encoder settings from an
+ * {@link AudioSpec} for the given {@link AudioSource.Settings} using the provided
+ * {@link CamcorderProfileProxy}.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public final class AudioEncoderConfigCamcorderProfileResolver implements
+ Supplier<AudioEncoderConfig> {
+
+ private static final String TAG = "AudioEncCmcrdrPrflRslvr";
+
+ private final String mMimeType;
+ private final int mAudioProfile;
+ private final AudioSpec mAudioSpec;
+ private final AudioSource.Settings mAudioSourceSettings;
+ private final CamcorderProfileProxy mCamcorderProfile;
+
+ /**
+ * Constructor for an AudioEncoderConfigCamcorderProfileResolver.
+ *
+ * @param mimeType The mime type for the audio encoder
+ * @param audioProfile The profile required for the audio encoder
+ * @param audioSpec The {@link AudioSpec} which defines the settings that should be
+ * used with the audio encoder.
+ * @param audioSourceSettings The settings used to configure the source of audio.
+ * @param camcorderProfile The {@link CamcorderProfileProxy} used to resolve automatic and
+ * range settings.
+ */
+ public AudioEncoderConfigCamcorderProfileResolver(@NonNull String mimeType,
+ int audioProfile, @NonNull AudioSpec audioSpec,
+ @NonNull AudioSource.Settings audioSourceSettings,
+ @NonNull CamcorderProfileProxy camcorderProfile) {
+ mMimeType = mimeType;
+ mAudioProfile = audioProfile;
+ mAudioSpec = audioSpec;
+ mAudioSourceSettings = audioSourceSettings;
+ mCamcorderProfile = camcorderProfile;
+ }
+
+ @Override
+ @NonNull
+ public AudioEncoderConfig get() {
+ Logger.d(TAG, "Using resolved AUDIO bitrate from CamcorderProfile");
+ Range<Integer> audioSpecBitrateRange = mAudioSpec.getBitrate();
+ int resolvedBitrate = AudioConfigUtil.scaleAndClampBitrate(
+ mCamcorderProfile.getAudioBitRate(),
+ mAudioSourceSettings.getChannelCount(), mCamcorderProfile.getAudioChannels(),
+ mAudioSourceSettings.getSampleRate(), mCamcorderProfile.getAudioSampleRate(),
+ audioSpecBitrateRange);
+
+ return AudioEncoderConfig.builder()
+ .setMimeType(mMimeType)
+ .setProfile(mAudioProfile)
+ .setChannelCount(mAudioSourceSettings.getChannelCount())
+ .setSampleRate(mAudioSourceSettings.getSampleRate())
+ .setBitrate(resolvedBitrate)
+ .build();
+ }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioEncoderConfigDefaultResolver.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioEncoderConfigDefaultResolver.java
new file mode 100644
index 0000000..0b756a8
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioEncoderConfigDefaultResolver.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config;
+
+import android.util.Range;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
+import androidx.camera.video.AudioSpec;
+import androidx.camera.video.internal.AudioSource;
+import androidx.camera.video.internal.encoder.AudioEncoderConfig;
+import androidx.core.util.Supplier;
+
+/**
+ * An {@link AudioEncoderConfig} supplier that resolves requested encoder settings from a
+ * {@link AudioSpec} for the given {@link AudioSource.Settings} using pre-defined default values.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public final class AudioEncoderConfigDefaultResolver implements Supplier<AudioEncoderConfig> {
+
+ private static final String TAG = "AudioEncCfgDefaultRslvr";
+
+ private final String mMimeType;
+ private final int mAudioProfile;
+ private final AudioSpec mAudioSpec;
+ private final AudioSource.Settings mAudioSourceSettings;
+
+ // Base config based on generic 720p AAC(LC) quality will be scaled by actual source settings.
+ // TODO: These should vary based on quality/codec and be derived from actual devices
+ private static final int AUDIO_BITRATE_BASE = 156000;
+ private static final int AUDIO_CHANNEL_COUNT_BASE = 2;
+ private static final int AUDIO_SAMPLE_RATE_BASE = 48000;
+
+ /**
+ * Constructor for an AudioEncoderConfigDefaultResolver.
+ *
+ * @param mimeType The mime type for the audio encoder
+ * @param audioProfile The profile required for the audio encoder
+ * @param audioSpec The {@link AudioSpec} which defines the settings that should be
+ * used with the audio encoder.
+ * @param audioSourceSettings The settings used to configure the source of audio.
+ */
+ public AudioEncoderConfigDefaultResolver(@NonNull String mimeType,
+ int audioProfile, @NonNull AudioSpec audioSpec,
+ @NonNull AudioSource.Settings audioSourceSettings) {
+ mMimeType = mimeType;
+ mAudioProfile = audioProfile;
+ mAudioSpec = audioSpec;
+ mAudioSourceSettings = audioSourceSettings;
+ }
+
+ @Override
+ @NonNull
+ public AudioEncoderConfig get() {
+ Range<Integer> audioSpecBitrateRange = mAudioSpec.getBitrate();
+ Logger.d(TAG, "Using fallback AUDIO bitrate");
+ // We have no other information to go off of. Scale based on fallback defaults.
+ int resolvedBitrate = AudioConfigUtil.scaleAndClampBitrate(
+ AUDIO_BITRATE_BASE,
+ mAudioSourceSettings.getChannelCount(), AUDIO_CHANNEL_COUNT_BASE,
+ mAudioSourceSettings.getSampleRate(), AUDIO_SAMPLE_RATE_BASE,
+ audioSpecBitrateRange);
+
+ return AudioEncoderConfig.builder()
+ .setMimeType(mMimeType)
+ .setProfile(mAudioProfile)
+ .setChannelCount(mAudioSourceSettings.getChannelCount())
+ .setSampleRate(mAudioSourceSettings.getSampleRate())
+ .setBitrate(resolvedBitrate)
+ .build();
+ }
+
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioSourceSettingsCamcorderProfileResolver.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioSourceSettingsCamcorderProfileResolver.java
new file mode 100644
index 0000000..8d6de0f
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioSourceSettingsCamcorderProfileResolver.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config;
+
+import android.util.Range;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
+import androidx.camera.core.impl.CamcorderProfileProxy;
+import androidx.camera.video.AudioSpec;
+import androidx.camera.video.internal.AudioSource;
+import androidx.core.util.Supplier;
+
+/**
+ * An {@link AudioSource.Settings} supplier that resolves requested source settings from an
+ * {@link AudioSpec} using a {@link CamcorderProfileProxy}.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public final class AudioSourceSettingsCamcorderProfileResolver implements
+ Supplier<AudioSource.Settings> {
+
+ private static final String TAG = "AudioSrcCmcrdrPrflRslvr";
+
+ private final AudioSpec mAudioSpec;
+ private final CamcorderProfileProxy mCamcorderProfile;
+
+ /**
+ * Constructor for an AudioSourceSettingsCamcorderProfileResolver.
+ *
+ * @param camcorderProfile The {@link CamcorderProfileProxy} used to resolve automatic and
+ * range settings.
+ * @param audioSpec The {@link AudioSpec} which defines the settings that should be
+ * used with the audio source.
+ */
+ public AudioSourceSettingsCamcorderProfileResolver(@NonNull AudioSpec audioSpec,
+ @NonNull CamcorderProfileProxy camcorderProfile) {
+ mAudioSpec = audioSpec;
+ mCamcorderProfile = camcorderProfile;
+ }
+
+ @Override
+ @NonNull
+ public AudioSource.Settings get() {
+ // Resolve audio source
+ int resolvedAudioSource = AudioConfigUtil.resolveAudioSource(mAudioSpec);
+
+ // Resolve source format
+ int resolvedSourceFormat = AudioConfigUtil.resolveAudioSourceFormat(
+ mAudioSpec);
+
+ int audioSpecChannelCount = mAudioSpec.getChannelCount();
+ Range<Integer> audioSpecSampleRate = mAudioSpec.getSampleRate();
+ int resolvedSampleRate;
+ int resolvedChannelCount;
+ int camcorderProfileChannelCount = mCamcorderProfile.getAudioChannels();
+ if (audioSpecChannelCount == AudioSpec.CHANNEL_COUNT_AUTO) {
+ resolvedChannelCount = camcorderProfileChannelCount;
+ Logger.d(TAG, "Resolved AUDIO channel count from CamcorderProfile: "
+ + resolvedChannelCount);
+ } else {
+ resolvedChannelCount = audioSpecChannelCount;
+ Logger.d(TAG, "Media spec AUDIO channel count overrides CamcorderProfile "
+ + "[CamcorderProfile channel count: " + camcorderProfileChannelCount
+ + ", Resolved Channel Count: " + resolvedChannelCount + "]");
+ }
+
+ int camcorderProfileAudioSampleRate = mCamcorderProfile.getAudioSampleRate();
+ resolvedSampleRate = AudioConfigUtil.selectSampleRateOrNearestSupported(
+ audioSpecSampleRate, resolvedChannelCount, resolvedSourceFormat,
+ camcorderProfileAudioSampleRate);
+ Logger.d(TAG, "Using resolved AUDIO sample rate or nearest supported from "
+ + "CamcorderProfile: " + resolvedSampleRate + "Hz. [CamcorderProfile sample rate: "
+ + camcorderProfileAudioSampleRate + "Hz]");
+
+ return AudioSource.Settings.builder()
+ .setAudioSource(resolvedAudioSource)
+ .setAudioFormat(resolvedSourceFormat)
+ .setChannelCount(resolvedChannelCount)
+ .setSampleRate(resolvedSampleRate)
+ .build();
+ }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioSourceSettingsDefaultResolver.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioSourceSettingsDefaultResolver.java
new file mode 100644
index 0000000..70fbad1
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/AudioSourceSettingsDefaultResolver.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config;
+
+import android.util.Range;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
+import androidx.camera.video.AudioSpec;
+import androidx.camera.video.internal.AudioSource;
+import androidx.core.util.Supplier;
+
+/**
+ * An {@link AudioSource.Settings} supplier that resolves requested source settings from an
+ * {@link AudioSpec} using pre-defined default values.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public final class AudioSourceSettingsDefaultResolver implements Supplier<AudioSource.Settings> {
+
+ private static final String TAG = "DefAudioSrcResolver";
+
+ private final AudioSpec mAudioSpec;
+
+ /**
+ * Constructor for an AudioSourceSettingsDefaultResolver.
+ *
+ * @param audioSpec The {@link AudioSpec} which defines the settings that should be used with
+ * the audio source.
+ */
+ public AudioSourceSettingsDefaultResolver(@NonNull AudioSpec audioSpec) {
+ mAudioSpec = audioSpec;
+ }
+
+ @Override
+ @NonNull
+ public AudioSource.Settings get() {
+ // Resolve audio source
+ int resolvedAudioSource = AudioConfigUtil.resolveAudioSource(mAudioSpec);
+
+ // Resolve source format
+ int resolvedSourceFormat = AudioConfigUtil.resolveAudioSourceFormat(mAudioSpec);
+
+ // Resolve channel count
+ int audioSpecChannelCount = mAudioSpec.getChannelCount();
+ int resolvedChannelCount;
+ if (audioSpecChannelCount == AudioSpec.CHANNEL_COUNT_AUTO) {
+ resolvedChannelCount = AudioConfigUtil.AUDIO_CHANNEL_COUNT_DEFAULT;
+ Logger.d(TAG, "Using fallback AUDIO channel count: " + resolvedChannelCount);
+ } else {
+ resolvedChannelCount = audioSpecChannelCount;
+ Logger.d(TAG, "Using supplied AUDIO channel count: " + audioSpecChannelCount);
+ }
+
+ // Resolve sample rate
+ Range<Integer> audioSpecSampleRateRange = mAudioSpec.getSampleRate();
+ int resolvedSampleRate;
+ if (AudioSpec.SAMPLE_RATE_RANGE_AUTO.equals(audioSpecSampleRateRange)) {
+ resolvedSampleRate = AudioConfigUtil.AUDIO_SAMPLE_RATE_DEFAULT;
+ Logger.d(TAG, "Using fallback AUDIO sample rate: " + resolvedSampleRate + "Hz");
+ } else {
+ resolvedSampleRate = AudioConfigUtil.selectSampleRateOrNearestSupported(
+ audioSpecSampleRateRange,
+ resolvedChannelCount, resolvedSourceFormat,
+ audioSpecSampleRateRange.getUpper());
+ Logger.d(TAG, "Using AUDIO sample rate resolved from AudioSpec: " + resolvedSampleRate
+ + "Hz");
+ }
+
+ return AudioSource.Settings.builder()
+ .setAudioSource(resolvedAudioSource)
+ .setAudioFormat(resolvedSourceFormat)
+ .setChannelCount(resolvedChannelCount)
+ .setSampleRate(resolvedSampleRate)
+ .build();
+ }
+
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/MimeInfo.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/MimeInfo.java
new file mode 100644
index 0000000..4fbbd71
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/MimeInfo.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.CamcorderProfileProxy;
+import androidx.camera.video.internal.encoder.EncoderConfig;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * Data class containing information about a media mime.
+ *
+ * <p>The information included in this class can include the mime type, profile and any
+ * compatible configuration types that can be used to resolve settings, such as
+ * {@link androidx.camera.core.impl.CamcorderProfileProxy}.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@AutoValue
+public abstract class MimeInfo {
+
+ /** Returns the mime type. */
+ @NonNull
+ public abstract String getMimeType();
+
+ /**
+ * Returns the profile for the given mime.
+ *
+ * <p>The returned integer will generally come from
+ * {@link android.media.MediaCodecInfo.CodecProfileLevel}, or if no profile is required,
+ * {@link androidx.camera.video.internal.encoder.EncoderConfig#CODEC_PROFILE_NONE}.
+ */
+ public abstract int getProfile();
+
+ /**
+ * Returns compatible {@link CamcorderProfileProxy} that can be used to resolve settings.
+ *
+ * <p>If no camcorder profile is provided, returns {@code null}
+ */
+ @Nullable
+ public abstract CamcorderProfileProxy getCompatibleCamcorderProfile();
+
+ /** Creates a builder for the given mime type */
+ @NonNull
+ public static Builder builder(@NonNull String mimeType) {
+ return new AutoValue_MimeInfo.Builder()
+ .setMimeType(mimeType)
+ .setProfile(EncoderConfig.CODEC_PROFILE_NONE);
+ }
+
+ /** A Builder for a {@link androidx.camera.video.internal.config.MimeInfo} */
+ @AutoValue.Builder
+ public abstract static class Builder {
+
+ // Package-private since this should be passed to builder factory method.
+ @NonNull
+ abstract Builder setMimeType(@NonNull String mimeType);
+
+ /** Sets the mime profile */
+ @NonNull
+ public abstract Builder setProfile(int profile);
+
+ /** Sets a compatible camcorder profile */
+ @NonNull
+ public abstract Builder setCompatibleCamcorderProfile(
+ @Nullable CamcorderProfileProxy camcorderProfile);
+
+ /** Builds the {@link androidx.camera.video.internal.config.MimeInfo}. */
+ @NonNull
+ public abstract MimeInfo build();
+ }
+
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoConfigUtil.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoConfigUtil.java
new file mode 100644
index 0000000..02dca7c3
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoConfigUtil.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config;
+
+import android.util.Range;
+import android.util.Rational;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
+import androidx.camera.video.VideoSpec;
+
+/**
+ * A collection of utilities used for resolving and debugging video configurations.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public final class VideoConfigUtil {
+ private static final String TAG = "VideoConfigUtil";
+
+ private static final int VIDEO_FRAME_RATE_FIXED_DEFAULT = 30;
+
+ // Should not be instantiated.
+ private VideoConfigUtil() {
+ }
+
+ static int resolveFrameRate(@NonNull VideoSpec videoSpec) {
+ // TODO(b/177918193): We currently cannot communicate the frame rate to the camera,
+ // so we only support 30fps. This should come from MediaSpec or use
+ // CamcorderProfile.videoFrameRate if set to AUTO framerate.
+ Range<Integer> videoSpecFrameRateRange = videoSpec.getFrameRate();
+ int resolvedFrameRate = VIDEO_FRAME_RATE_FIXED_DEFAULT;
+ if (VideoSpec.FRAME_RATE_RANGE_AUTO.equals(videoSpecFrameRateRange)
+ || videoSpecFrameRateRange.contains(VIDEO_FRAME_RATE_FIXED_DEFAULT)) {
+ Logger.d(TAG, "Using single supported VIDEO frame rate: " + resolvedFrameRate);
+ } else {
+ Logger.w(TAG,
+ "Requested frame rate range does not include single supported frame rate. "
+ + "Ignoring range. [range: " + videoSpecFrameRateRange + " supported "
+ + "frame rate: " + resolvedFrameRate + "]");
+ }
+
+ return resolvedFrameRate;
+ }
+
+ static int scaleAndClampBitrate(
+ int baseBitrate,
+ int actualFrameRate, int baseFrameRate,
+ int actualWidth, int baseWidth,
+ int actualHeight, int baseHeight,
+ @NonNull Range<Integer> clampedRange) {
+ // Scale bitrate to match current frame rate
+ Rational frameRateRatio = new Rational(actualFrameRate, baseFrameRate);
+ // Scale bitrate depending on number of actual pixels relative to profile's
+ // number of pixels.
+ // TODO(b/191678894): This should come from the eventual crop rectangle rather
+ // than the full surface size.
+ Rational widthRatio = new Rational(actualWidth, baseWidth);
+ Rational heightRatio = new Rational(actualHeight, baseHeight);
+ int resolvedBitrate =
+ (int) (baseBitrate * frameRateRatio.doubleValue() * widthRatio.doubleValue()
+ * heightRatio.doubleValue());
+
+ String debugString = "";
+ if (Logger.isDebugEnabled(TAG)) {
+ debugString = String.format("Base Bitrate(%dbps) * Frame Rate Ratio(%d / %d) * Width "
+ + "Ratio(%d / %d) * Height Ratio(%d / %d) = %d", baseBitrate,
+ actualFrameRate,
+ baseFrameRate, actualWidth, baseWidth, actualHeight, baseHeight,
+ resolvedBitrate);
+ }
+
+ if (!VideoSpec.BITRATE_RANGE_AUTO.equals(clampedRange)) {
+ // Clamp the resolved bitrate
+ resolvedBitrate = clampedRange.clamp(resolvedBitrate);
+ if (Logger.isDebugEnabled(TAG)) {
+ debugString += String.format("\nClamped to range %s -> %dbps", clampedRange,
+ resolvedBitrate);
+ }
+ }
+ Logger.d(TAG, debugString);
+ return resolvedBitrate;
+ }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigCamcorderProfileResolver.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigCamcorderProfileResolver.java
new file mode 100644
index 0000000..ba39b52
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigCamcorderProfileResolver.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config;
+
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
+import androidx.camera.core.impl.CamcorderProfileProxy;
+import androidx.camera.video.VideoSpec;
+import androidx.camera.video.internal.encoder.VideoEncoderConfig;
+import androidx.core.util.Supplier;
+
+/**
+ * A {@link VideoEncoderConfig} supplier that resolves requested encoder settings from a
+ * {@link VideoSpec} for the given surface {@link Size} using the provided
+ * {@link CamcorderProfileProxy}.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class VideoEncoderConfigCamcorderProfileResolver implements Supplier<VideoEncoderConfig> {
+
+ private static final String TAG = "VidEncCmcrdrPrflRslvr";
+
+ private final String mMimeType;
+ private final VideoSpec mVideoSpec;
+ private final Size mSurfaceSize;
+ private final CamcorderProfileProxy mCamcorderProfile;
+
+ /**
+ * Constructor for a VideoEncoderConfigCamcorderProfileResolver.
+ *
+ * @param mimeType The mime type for the video encoder
+ * @param videoSpec The {@link VideoSpec} which defines the settings that should be
+ * used with the video encoder.
+ * @param surfaceSize The size of the surface required by the camera for the video encoder.
+ * @param camcorderProfile The {@link CamcorderProfileProxy} used to resolve automatic and
+ * range settings.
+ */
+ public VideoEncoderConfigCamcorderProfileResolver(@NonNull String mimeType,
+ @NonNull VideoSpec videoSpec,
+ @NonNull Size surfaceSize,
+ @NonNull CamcorderProfileProxy camcorderProfile) {
+ mMimeType = mimeType;
+ mVideoSpec = videoSpec;
+ mSurfaceSize = surfaceSize;
+ mCamcorderProfile = camcorderProfile;
+ }
+
+ @Override
+ @NonNull
+ public VideoEncoderConfig get() {
+ int resolvedFrameRate = VideoConfigUtil.resolveFrameRate(mVideoSpec);
+
+ Range<Integer> videoSpecBitrateRange = mVideoSpec.getBitrate();
+ Logger.d(TAG, "Using resolved VIDEO bitrate from CamcorderProfile");
+ int resolvedBitrate = VideoConfigUtil.scaleAndClampBitrate(
+ mCamcorderProfile.getVideoBitRate(),
+ resolvedFrameRate, mCamcorderProfile.getVideoFrameRate(),
+ mSurfaceSize.getWidth(), mCamcorderProfile.getVideoFrameWidth(),
+ mSurfaceSize.getHeight(), mCamcorderProfile.getVideoFrameHeight(),
+ videoSpecBitrateRange);
+
+ return VideoEncoderConfig.builder()
+ .setMimeType(mMimeType)
+ .setResolution(mSurfaceSize)
+ .setBitrate(resolvedBitrate)
+ .setFrameRate(resolvedFrameRate)
+ .build();
+ }
+}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolver.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolver.java
new file mode 100644
index 0000000..d61649f
--- /dev/null
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/VideoEncoderConfigDefaultResolver.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.video.internal.config;
+
+import android.util.Range;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
+import androidx.camera.video.VideoSpec;
+import androidx.camera.video.internal.encoder.VideoEncoderConfig;
+import androidx.core.util.Supplier;
+
+/**
+ * A {@link VideoEncoderConfig} supplier that resolves requested encoder settings from a
+ * {@link VideoSpec} for the given surface {@link Size} using pre-defined default values.
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class VideoEncoderConfigDefaultResolver implements Supplier<VideoEncoderConfig> {
+
+ private static final String TAG = "VidEncCfgDefaultRslvr";
+
+ // Base config based on generic 720p H264 quality will be scaled by actual source settings.
+ // TODO: These should vary based on quality/codec and be derived from actual devices
+ private static final int VIDEO_BITRATE_BASE = 14000000;
+ private static final Size VIDEO_SIZE_BASE = new Size(1280, 720);
+ private static final int VIDEO_FRAME_RATE_BASE = 30;
+
+ private final String mMimeType;
+ private final VideoSpec mVideoSpec;
+ private final Size mSurfaceSize;
+
+ /**
+ * Constructor for a VideoEncoderConfigDefaultResolver.
+ *
+ * @param mimeType The mime type for the video encoder
+ * @param videoSpec The {@link VideoSpec} which defines the settings that should be used with
+ * the video encoder.
+ * @param surfaceSize The size of the surface required by the camera for the video encoder.
+ */
+ public VideoEncoderConfigDefaultResolver(@NonNull String mimeType,
+ @NonNull VideoSpec videoSpec, @NonNull Size surfaceSize) {
+ mMimeType = mimeType;
+ mVideoSpec = videoSpec;
+ mSurfaceSize = surfaceSize;
+ }
+
+ @Override
+ @NonNull
+ public VideoEncoderConfig get() {
+ int resolvedFrameRate = VideoConfigUtil.resolveFrameRate(mVideoSpec);
+
+ Range<Integer> videoSpecBitrateRange = mVideoSpec.getBitrate();
+ Logger.d(TAG, "Using fallback VIDEO bitrate");
+ // We have no other information to go off of. Scale based on fallback defaults.
+ int resolvedBitrate = VideoConfigUtil.scaleAndClampBitrate(
+ VIDEO_BITRATE_BASE,
+ resolvedFrameRate, VIDEO_FRAME_RATE_BASE,
+ mSurfaceSize.getWidth(), VIDEO_SIZE_BASE.getWidth(),
+ mSurfaceSize.getHeight(), VIDEO_SIZE_BASE.getHeight(),
+ videoSpecBitrateRange);
+
+ return VideoEncoderConfig.builder()
+ .setMimeType(mMimeType)
+ .setResolution(mSurfaceSize)
+ .setBitrate(resolvedBitrate)
+ .setFrameRate(resolvedFrameRate)
+ .build();
+ }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/package-info.java
similarity index 68%
copy from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt
copy to camera/camera-video/src/main/java/androidx/camera/video/internal/config/package-info.java
index d721bff..f350cca 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/config/package-info.java
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+package androidx.camera.video.internal.config;
-import androidx.compose.foundation.lazy.layout.LazyLayoutItemsProvider
-
-internal interface LazyListItemsProvider : LazyLayoutItemsProvider {
- /** The list of indexes of the sticky header items */
- val headerIndexes: List<Int>
-}
+import androidx.annotation.RestrictTo;
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/AudioEncoderConfig.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/AudioEncoderConfig.java
index 418678a..3af219d 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/AudioEncoderConfig.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/AudioEncoderConfig.java
@@ -16,7 +16,6 @@
package androidx.camera.video.internal.encoder;
-import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import androidx.annotation.NonNull;
@@ -24,6 +23,8 @@
import com.google.auto.value.AutoValue;
+import java.util.Objects;
+
/** {@inheritDoc} */
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
@AutoValue
@@ -36,7 +37,8 @@
/** Returns a build for this config. */
@NonNull
public static Builder builder() {
- return new AutoValue_AudioEncoderConfig.Builder();
+ return new AutoValue_AudioEncoderConfig.Builder()
+ .setProfile(EncoderConfig.CODEC_PROFILE_NONE);
}
/** {@inheritDoc} */
@@ -44,6 +46,14 @@
@NonNull
public abstract String getMimeType();
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Default is {@link EncoderConfig#CODEC_PROFILE_NONE}.
+ */
+ @Override
+ public abstract int getProfile();
+
/** Gets the bitrate. */
public abstract int getBitrate();
@@ -60,10 +70,14 @@
MediaFormat mediaFormat = MediaFormat.createAudioFormat(getMimeType(), getSampleRate(),
getChannelCount());
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, getBitrate());
- if (getMimeType().equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
- mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE,
- MediaCodecInfo.CodecProfileLevel.AACObjectLC);
+ if (getProfile() != CODEC_PROFILE_NONE) {
+ if (getMimeType().equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+ mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, getProfile());
+ } else {
+ mediaFormat.setInteger(MediaFormat.KEY_PROFILE, getProfile());
+ }
}
+
return mediaFormat;
}
@@ -74,10 +88,21 @@
Builder() {
}
- /** Sets the mime type. */
+ /**
+ * Sets the mime type.
+ *
+ * <p>If the mime type is set to AAC, i.e. {@link MediaFormat#MIMETYPE_AUDIO_AAC}, then a
+ * profile must be set with {@link #setProfile(int)}. If a profile isn't set or is set to
+ * {@link EncoderConfig#CODEC_PROFILE_NONE}, an IllegalArgumentException will be thrown by
+ * {@link #build()}.
+ */
@NonNull
public abstract Builder setMimeType(@NonNull String mimeType);
+ /** Sets (optional) profile for the mime type specified by {@link #setMimeType(String)}. */
+ @NonNull
+ public abstract Builder setProfile(int profile);
+
/** Sets the bitrate. */
@NonNull
public abstract Builder setBitrate(int bitrate);
@@ -90,8 +115,20 @@
@NonNull
public abstract Builder setChannelCount(int channelCount);
+ @NonNull
+ abstract AudioEncoderConfig autoBuild();
+
/** Builds the config instance. */
@NonNull
- public abstract AudioEncoderConfig build();
+ public AudioEncoderConfig build() {
+ AudioEncoderConfig config = autoBuild();
+ if (Objects.equals(config.getMimeType(), MediaFormat.MIMETYPE_AUDIO_AAC)
+ && config.getProfile() == CODEC_PROFILE_NONE) {
+ throw new IllegalArgumentException("Encoder mime set to AAC, but no AAC profile "
+ + "was provided.");
+ }
+
+ return config;
+ }
}
}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderConfig.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderConfig.java
index 8c7436c..5a1a228 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderConfig.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/EncoderConfig.java
@@ -20,6 +20,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.CamcorderProfileProxy;
/**
* The configuration represents the required parameters to configure an encoder.
@@ -28,6 +29,10 @@
*/
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public interface EncoderConfig {
+
+ /** Constant corresponding to no profile for the encoder */
+ int CODEC_PROFILE_NONE = CamcorderProfileProxy.CODEC_PROFILE_NONE;
+
/**
* The mime type of the encoder.
*
@@ -39,6 +44,17 @@
String getMimeType();
/**
+ * The (optional) profile for the mime type returned by {@link #getMimeType()}.
+ *
+ * <p>For example, for AAC-ELD (enhanced low delay) audio encoder, the mime type is
+ * "audio/mp4a-latm" and the profile needs to be
+ * {@link android.media.MediaCodecInfo.CodecProfileLevel#AACObjectELD}.
+ *
+ * <p>Not all mime types require a profile, so this is optional.
+ */
+ int getProfile();
+
+ /**
* Transfers the config to a {@link MediaFormat}.
*
* @return the result {@link MediaFormat}
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/VideoEncoderConfig.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/VideoEncoderConfig.java
index cdcb3ba..d3ff4af 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/VideoEncoderConfig.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/encoder/VideoEncoderConfig.java
@@ -16,6 +16,7 @@
package androidx.camera.video.internal.encoder;
+import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.util.Size;
@@ -29,6 +30,10 @@
@AutoValue
public abstract class VideoEncoderConfig implements EncoderConfig {
+ private static final int VIDEO_INTRA_FRAME_INTERVAL_DEFAULT = 1;
+ private static final int VIDEO_COLOR_FORMAT_DEFAULT =
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
+
// Restrict constructor to same package
VideoEncoderConfig() {
}
@@ -36,14 +41,19 @@
/** Returns a build for this config. */
@NonNull
public static Builder builder() {
- return new AutoValue_VideoEncoderConfig.Builder();
+ return new AutoValue_VideoEncoderConfig.Builder()
+ .setProfile(EncoderConfig.CODEC_PROFILE_NONE)
+ .setIFrameInterval(VIDEO_INTRA_FRAME_INTERVAL_DEFAULT)
+ .setColorFormat(VIDEO_COLOR_FORMAT_DEFAULT);
}
- /** {@inheritDoc} */
@Override
@NonNull
public abstract String getMimeType();
+ @Override
+ public abstract int getProfile();
+
/** Gets the resolution. */
@NonNull
public abstract Size getResolution();
@@ -71,6 +81,9 @@
format.setInteger(MediaFormat.KEY_BIT_RATE, getBitrate());
format.setInteger(MediaFormat.KEY_FRAME_RATE, getFrameRate());
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, getIFrameInterval());
+ if (getProfile() != EncoderConfig.CODEC_PROFILE_NONE) {
+ format.setInteger(MediaFormat.KEY_PROFILE, getProfile());
+ }
return format;
}
@@ -85,6 +98,10 @@
@NonNull
public abstract Builder setMimeType(@NonNull String mimeType);
+ /** Sets (optional) profile for the mime type specified by {@link #setMimeType(String)}. */
+ @NonNull
+ public abstract Builder setProfile(int profile);
+
/** Sets the resolution. */
@NonNull
public abstract Builder setResolution(@NonNull Size resolution);
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/QualitySelectorTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/QualitySelectorTest.kt
index aaffc65..8e7dd14 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/QualitySelectorTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/QualitySelectorTest.kt
@@ -26,17 +26,6 @@
import androidx.camera.testing.CamcorderProfileUtil.RESOLUTION_720P
import androidx.camera.testing.fakes.FakeCamcorderProfileProvider
import androidx.camera.testing.fakes.FakeCameraInfoInternal
-import androidx.camera.video.QualitySelector.FALLBACK_STRATEGY_HIGHER
-import androidx.camera.video.QualitySelector.FALLBACK_STRATEGY_LOWER
-import androidx.camera.video.QualitySelector.FALLBACK_STRATEGY_STRICTLY_HIGHER
-import androidx.camera.video.QualitySelector.FALLBACK_STRATEGY_STRICTLY_LOWER
-import androidx.camera.video.QualitySelector.QUALITY_FHD
-import androidx.camera.video.QualitySelector.QUALITY_HD
-import androidx.camera.video.QualitySelector.QUALITY_HIGHEST
-import androidx.camera.video.QualitySelector.QUALITY_LOWEST
-import androidx.camera.video.QualitySelector.QUALITY_NONE
-import androidx.camera.video.QualitySelector.QUALITY_SD
-import androidx.camera.video.QualitySelector.QUALITY_UHD
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Test
@@ -45,8 +34,6 @@
import org.robolectric.annotation.Config
import org.robolectric.annotation.internal.DoNotInstrument
-private const val NOT_QUALITY_CONSTANT = -111
-private const val NOT_FALLBACK_STRATEGY_CONSTANT = -1
private const val CAMERA_ID_0 = "0"
private const val CAMERA_ID_1 = "1"
private val CAMERA_0_PROFILE_HIGH = CamcorderProfileUtil.asHighQuality(PROFILE_2160P)
@@ -80,12 +67,12 @@
@Test
fun getSortedQualities_fromLargeToSmall() {
- val sortedQualities = QualitySelector.getSortedQualities()
+ val sortedQualities = Quality.getSortedQualities()
- assertThat(sortedQualities[0]).isEqualTo(QUALITY_UHD)
- assertThat(sortedQualities[1]).isEqualTo(QUALITY_FHD)
- assertThat(sortedQualities[2]).isEqualTo(QUALITY_HD)
- assertThat(sortedQualities[3]).isEqualTo(QUALITY_SD)
+ assertThat(sortedQualities[0]).isEqualTo(Quality.UHD)
+ assertThat(sortedQualities[1]).isEqualTo(Quality.FHD)
+ assertThat(sortedQualities[2]).isEqualTo(Quality.HD)
+ assertThat(sortedQualities[3]).isEqualTo(Quality.SD)
}
@Test
@@ -93,129 +80,94 @@
// camera0 supports 2160P(UHD) and 720P(HD)
val supportedQualities = QualitySelector.getSupportedQualities(cameraInfo0)
- assertThat(supportedQualities[0]).isEqualTo(QUALITY_UHD)
- assertThat(supportedQualities[1]).isEqualTo(QUALITY_HD)
+ assertThat(supportedQualities[0]).isEqualTo(Quality.UHD)
+ assertThat(supportedQualities[1]).isEqualTo(Quality.HD)
}
@Test
fun isQualitySupported_returnCorrectResult() {
// camera0 supports 2160P(UHD) and 720P(HD)
- assertThat(QualitySelector.isQualitySupported(cameraInfo0, QUALITY_NONE)).isFalse()
- assertThat(QualitySelector.isQualitySupported(cameraInfo0, QUALITY_HIGHEST)).isTrue()
- assertThat(QualitySelector.isQualitySupported(cameraInfo0, QUALITY_LOWEST)).isTrue()
- assertThat(QualitySelector.isQualitySupported(cameraInfo0, QUALITY_UHD)).isTrue()
- assertThat(QualitySelector.isQualitySupported(cameraInfo0, QUALITY_FHD)).isFalse()
- assertThat(QualitySelector.isQualitySupported(cameraInfo0, QUALITY_HD)).isTrue()
- assertThat(QualitySelector.isQualitySupported(cameraInfo0, QUALITY_SD)).isFalse()
+ assertThat(QualitySelector.isQualitySupported(cameraInfo0, Quality.HIGHEST)).isTrue()
+ assertThat(QualitySelector.isQualitySupported(cameraInfo0, Quality.LOWEST)).isTrue()
+ assertThat(QualitySelector.isQualitySupported(cameraInfo0, Quality.UHD)).isTrue()
+ assertThat(QualitySelector.isQualitySupported(cameraInfo0, Quality.FHD)).isFalse()
+ assertThat(QualitySelector.isQualitySupported(cameraInfo0, Quality.HD)).isTrue()
+ assertThat(QualitySelector.isQualitySupported(cameraInfo0, Quality.SD)).isFalse()
}
@Test
fun getResolution_returnCorrectResolution() {
// camera0 supports 2160P(UHD) and 720P(HD)
assertThat(
- QualitySelector.getResolution(cameraInfo0, QUALITY_NONE)
- ).isNull()
- assertThat(
- QualitySelector.getResolution(cameraInfo0, QUALITY_HIGHEST)
+ QualitySelector.getResolution(cameraInfo0, Quality.HIGHEST)
).isEqualTo(RESOLUTION_2160P)
assertThat(
- QualitySelector.getResolution(cameraInfo0, QUALITY_LOWEST)
+ QualitySelector.getResolution(cameraInfo0, Quality.LOWEST)
).isEqualTo(RESOLUTION_720P)
assertThat(
- QualitySelector.getResolution(cameraInfo0, QUALITY_UHD)
+ QualitySelector.getResolution(cameraInfo0, Quality.UHD)
).isEqualTo(RESOLUTION_2160P)
assertThat(
- QualitySelector.getResolution(cameraInfo0, QUALITY_FHD)
+ QualitySelector.getResolution(cameraInfo0, Quality.FHD)
).isNull()
assertThat(
- QualitySelector.getResolution(cameraInfo0, QUALITY_HD)
+ QualitySelector.getResolution(cameraInfo0, Quality.HD)
).isEqualTo(RESOLUTION_720P)
assertThat(
- QualitySelector.getResolution(cameraInfo0, QUALITY_SD)
+ QualitySelector.getResolution(cameraInfo0, Quality.SD)
).isNull()
}
@Test
- fun of_setNonQualityConstant_throwException() {
+ fun fromOrderedList_containNull_throwException() {
// Assert.
assertThrows(IllegalArgumentException::class.java) {
// Act.
- QualitySelector.of(NOT_QUALITY_CONSTANT)
+ QualitySelector.fromOrderedList(listOf(Quality.FHD, null))
}
}
@Test
- fun thenTry_setNonQualityConstant_throwException() {
+ fun fromOrderedList_setEmptyQualityList_throwException() {
// Assert.
assertThrows(IllegalArgumentException::class.java) {
// Act.
- QualitySelector.firstTry(QUALITY_FHD)
- .thenTry(NOT_QUALITY_CONSTANT)
+ QualitySelector.fromOrderedList(emptyList())
}
}
@Test
- fun finallyTry_setNonQualityConstant_throwException() {
- // Assert.
- assertThrows(IllegalArgumentException::class.java) {
- // Act.
- QualitySelector.firstTry(QUALITY_FHD)
- .thenTry(QUALITY_HD)
- .finallyTry(NOT_QUALITY_CONSTANT)
- }
- }
-
- @Test
- fun of_setNonFallbackStrategyConstant_throwException() {
- // Assert.
- assertThrows(IllegalArgumentException::class.java) {
- // Act.
- QualitySelector.of(QUALITY_FHD, NOT_FALLBACK_STRATEGY_CONSTANT)
- }
- }
-
- @Test
- fun finallyTry_setNonFallbackStrategyConstant_throwException() {
- // Assert.
- assertThrows(IllegalArgumentException::class.java) {
- // Act.
- QualitySelector.firstTry(QUALITY_FHD)
- .finallyTry(QUALITY_HD, NOT_FALLBACK_STRATEGY_CONSTANT)
- }
- }
-
- @Test
- fun getPrioritizedQualities_byFirstTry() {
+ fun getPrioritizedQualities_selectSingleQuality() {
// Arrange.
// camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.firstTry(QUALITY_UHD)
- .finallyTry(QUALITY_SD)
+ val qualitySelector = QualitySelector.from(Quality.UHD)
// Act.
val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
// Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_UHD))
+ assertThat(qualities).isEqualTo(listOf(Quality.UHD))
}
@Test
- fun getPrioritizedQualities_byOf() {
+ fun getPrioritizedQualities_selectQualityByOrder() {
// Arrange.
// camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.of(QUALITY_UHD)
+ val qualitySelector =
+ QualitySelector.fromOrderedList(listOf(Quality.FHD, Quality.UHD, Quality.HD))
// Act.
val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
// Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_UHD))
+ assertThat(qualities).isEqualTo(listOf(Quality.UHD, Quality.HD))
}
@Test
- fun getPrioritizedQualities_byOf_noFallbackStrategy() {
+ fun getPrioritizedQualities_noFallbackStrategy() {
// Arrange.
// camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.of(QUALITY_FHD)
+ val qualitySelector = QualitySelector.from(Quality.FHD)
// Act.
val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
@@ -225,182 +177,146 @@
}
@Test
- fun getPrioritizedQualities_byOf_withFallbackStrategy() {
+ fun getPrioritizedQualities_withFallbackStrategy() {
// Arrange.
// camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.of(
- QUALITY_FHD,
- FALLBACK_STRATEGY_LOWER
+ val qualitySelector = QualitySelector.from(
+ Quality.FHD,
+ FallbackStrategy.lowerQualityOrHigherThan(Quality.FHD)
)
// Act.
val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
// Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_HD, QUALITY_UHD))
- }
-
- @Test
- fun getPrioritizedQualities_byThenTry() {
- // Arrange.
- // camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.firstTry(QUALITY_FHD)
- .thenTry(QUALITY_UHD)
- .finallyTry(QUALITY_FHD)
-
- // Act.
- val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
-
- // Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_UHD))
- }
-
- @Test
- fun getPrioritizedQualities_byFinallyTry() {
- // Arrange.
- // camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.firstTry(QUALITY_FHD)
- .thenTry(QUALITY_SD)
- .finallyTry(QUALITY_UHD)
-
- // Act.
- val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
-
- // Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_UHD))
- }
-
- @Test
- fun getPrioritizedQualities_byFinallyTry_noFallbackStrategy() {
- // Arrange.
- // camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.firstTry(QUALITY_FHD)
- .finallyTry(QUALITY_SD)
-
- // Act.
- val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
-
- // Assert.
- assertThat(qualities).isEmpty()
- }
-
- @Test
- fun getPrioritizedQualities_byFinallyTry_withFallbackStrategy() {
- // Arrange.
- // camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.firstTry(QUALITY_FHD)
- .finallyTry(QUALITY_SD, FALLBACK_STRATEGY_HIGHER)
-
- // Act.
- val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
-
- // Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_HD, QUALITY_UHD))
+ assertThat(qualities).isEqualTo(listOf(Quality.HD, Quality.UHD))
}
@Test
fun getPrioritizedQualities_containHighestQuality_addAll() {
// Arrange.
// camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.firstTry(QUALITY_FHD)
- .finallyTry(QUALITY_HIGHEST)
+ val qualitySelector = QualitySelector.fromOrderedList(listOf(Quality.FHD, Quality.HIGHEST))
// Act.
val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
// Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_UHD, QUALITY_HD))
+ assertThat(qualities).isEqualTo(listOf(Quality.UHD, Quality.HD))
}
@Test
fun getPrioritizedQualities_containLowestQuality_addAllReversely() {
// Arrange.
// camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.firstTry(QUALITY_FHD)
- .finallyTry(QUALITY_LOWEST)
+ val qualitySelector = QualitySelector.fromOrderedList(listOf(Quality.FHD, Quality.LOWEST))
// Act.
val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
// Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_HD, QUALITY_UHD))
+ assertThat(qualities).isEqualTo(listOf(Quality.HD, Quality.UHD))
}
@Test
fun getPrioritizedQualities_addDuplicateQuality_getSingleQualityWithCorrectOrder() {
// Arrange.
// camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.firstTry(QUALITY_SD)
- .thenTry(QUALITY_FHD)
- .thenTry(QUALITY_HD)
- .thenTry(QUALITY_UHD)
- // start duplicate qualities
- .thenTry(QUALITY_SD)
- .thenTry(QUALITY_HD)
- .thenTry(QUALITY_FHD)
- .thenTry(QUALITY_UHD)
- .thenTry(QUALITY_LOWEST)
- .thenTry(QUALITY_HIGHEST)
- .finallyTry(QUALITY_LOWEST, FALLBACK_STRATEGY_STRICTLY_HIGHER)
+ val qualitySelector = QualitySelector.fromOrderedList(
+ listOf(
+ Quality.SD,
+ Quality.FHD,
+ Quality.HD,
+ Quality.UHD,
+ // start duplicate qualities
+ Quality.SD,
+ Quality.HD,
+ Quality.FHD,
+ Quality.UHD,
+ Quality.LOWEST,
+ Quality.HIGHEST
+ ),
+ FallbackStrategy.higherQualityThan(Quality.LOWEST)
+ )
// Act.
val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
// Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_HD, QUALITY_UHD))
+ assertThat(qualities).isEqualTo(listOf(Quality.HD, Quality.UHD))
+ }
+
+ @Test
+ fun getPrioritizedQualities_fallbackLowerOrHigher_getLower() {
+ // Arrange.
+ // camera0 supports 2160P(UHD) and 720P(HD)
+ val qualitySelector = QualitySelector.from(
+ Quality.FHD,
+ FallbackStrategy.lowerQualityOrHigherThan(Quality.FHD)
+ )
+
+ // Act.
+ val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
+
+ // Assert.
+ assertThat(qualities).isEqualTo(listOf(Quality.HD, Quality.UHD))
+ }
+
+ @Test
+ fun getPrioritizedQualities_fallbackLowerOrHigher_fallbackQualityNotIncluded() {
+ // Arrange.
+ // camera0 supports 2160P(UHD) and 720P(HD)
+ val qualitySelector = QualitySelector.from(
+ Quality.SD,
+ FallbackStrategy.higherQualityThan(Quality.HD)
+ )
+
+ // Act.
+ val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
+
+ // Assert.
+ assertThat(qualities).isEqualTo(listOf(Quality.UHD))
+ }
+
+ @Test
+ fun getPrioritizedQualities_fallbackLowerOrHigher_getHigher() {
+ // Arrange.
+ // camera0 supports 2160P(UHD) and 720P(HD)
+ val qualitySelector = QualitySelector.from(
+ Quality.SD,
+ FallbackStrategy.lowerQualityOrHigherThan(Quality.SD)
+ )
+
+ // Act.
+ val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
+
+ // Assert.
+ assertThat(qualities).isEqualTo(listOf(Quality.HD, Quality.UHD))
}
@Test
fun getPrioritizedQualities_fallbackLower_getLower() {
// Arrange.
// camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.of(
- QUALITY_FHD, FALLBACK_STRATEGY_LOWER
+ val qualitySelector = QualitySelector.from(
+ Quality.FHD,
+ FallbackStrategy.lowerQualityThan(Quality.FHD)
)
// Act.
val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
// Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_HD, QUALITY_UHD))
+ assertThat(qualities).isEqualTo(listOf(Quality.HD))
}
@Test
- fun getPrioritizedQualities_fallbackLower_getHigher() {
+ fun getPrioritizedQualities_fallbackLower_getNone() {
// Arrange.
// camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.of(
- QUALITY_SD, FALLBACK_STRATEGY_LOWER
- )
-
- // Act.
- val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
-
- // Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_HD, QUALITY_UHD))
- }
-
- @Test
- fun getPrioritizedQualities_fallbackStrictLower_getLower() {
- // Arrange.
- // camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.of(
- QUALITY_FHD, FALLBACK_STRATEGY_STRICTLY_LOWER
- )
-
- // Act.
- val qualities = qualitySelector.getPrioritizedQualities(cameraInfo0)
-
- // Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_HD))
- }
-
- @Test
- fun getPrioritizedQualities_fallbackStrictLower_getNone() {
- // Arrange.
- // camera0 supports 2160P(UHD) and 720P(HD)
- val qualitySelector = QualitySelector.of(
- QUALITY_SD, FALLBACK_STRATEGY_STRICTLY_LOWER
+ val qualitySelector = QualitySelector.from(
+ Quality.SD,
+ FallbackStrategy.lowerQualityThan(Quality.SD)
)
// Act.
@@ -411,57 +327,76 @@
}
@Test
+ fun getPrioritizedQualities_fallbackHigherOrLower_getHigher() {
+ // Arrange.
+ // camera1 supports 1080P(FHD) and 480P(SD)
+ val qualitySelector = QualitySelector.from(
+ Quality.HD,
+ FallbackStrategy.higherQualityOrLowerThan(Quality.HD)
+ )
+
+ // Act.
+ val qualities = qualitySelector.getPrioritizedQualities(cameraInfo1)
+
+ // Assert.
+ assertThat(qualities).isEqualTo(listOf(Quality.FHD, Quality.SD))
+ }
+
+ @Test
+ fun getPrioritizedQualities_fallbackHigher_fallbackQualityNotIncluded() {
+ // Arrange.
+ // camera1 supports 1080P(FHD) and 480P(SD)
+ val qualitySelector = QualitySelector.from(
+ Quality.UHD,
+ FallbackStrategy.higherQualityThan(Quality.SD)
+ )
+
+ // Act.
+ val qualities = qualitySelector.getPrioritizedQualities(cameraInfo1)
+
+ // Assert.
+ assertThat(qualities).isEqualTo(listOf(Quality.FHD))
+ }
+
+ @Test
+ fun getPrioritizedQualities_fallbackHigherOrLower_getLower() {
+ // Arrange.
+ // camera1 supports 1080P(FHD) and 480P(SD)
+ val qualitySelector = QualitySelector.from(
+ Quality.UHD,
+ FallbackStrategy.higherQualityOrLowerThan(Quality.UHD)
+ )
+
+ // Act.
+ val qualities = qualitySelector.getPrioritizedQualities(cameraInfo1)
+
+ // Assert.
+ assertThat(qualities).isEqualTo(listOf(Quality.FHD, Quality.SD))
+ }
+
+ @Test
fun getPrioritizedQualities_fallbackHigher_getHigher() {
// Arrange.
// camera1 supports 1080P(FHD) and 480P(SD)
- val qualitySelector = QualitySelector.of(
- QUALITY_HD,
- FALLBACK_STRATEGY_HIGHER
+ val qualitySelector = QualitySelector.from(
+ Quality.HD,
+ FallbackStrategy.higherQualityThan(Quality.HD)
)
// Act.
val qualities = qualitySelector.getPrioritizedQualities(cameraInfo1)
// Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_FHD, QUALITY_SD))
+ assertThat(qualities).isEqualTo(listOf(Quality.FHD))
}
@Test
- fun getPrioritizedQualities_fallbackHigher_getLower() {
+ fun getPrioritizedQualities_fallbackHigher_getNone() {
// Arrange.
// camera1 supports 1080P(FHD) and 480P(SD)
- val qualitySelector = QualitySelector.of(
- QUALITY_UHD, FALLBACK_STRATEGY_HIGHER
- )
-
- // Act.
- val qualities = qualitySelector.getPrioritizedQualities(cameraInfo1)
-
- // Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_FHD, QUALITY_SD))
- }
-
- @Test
- fun getPrioritizedQualities_fallbackStrictHigher_getHigher() {
- // Arrange.
- // camera1 supports 1080P(FHD) and 480P(SD)
- val qualitySelector = QualitySelector.of(
- QUALITY_HD, FALLBACK_STRATEGY_STRICTLY_HIGHER
- )
-
- // Act.
- val qualities = qualitySelector.getPrioritizedQualities(cameraInfo1)
-
- // Assert.
- assertThat(qualities).isEqualTo(listOf(QUALITY_FHD))
- }
-
- @Test
- fun getPrioritizedQualities_fallbackStrictHigher_getNone() {
- // Arrange.
- // camera1 supports 1080P(FHD) and 480P(SD)
- val qualitySelector = QualitySelector.of(
- QUALITY_UHD, FALLBACK_STRATEGY_STRICTLY_HIGHER
+ val qualitySelector = QualitySelector.from(
+ Quality.UHD,
+ FallbackStrategy.higherQualityThan(Quality.UHD)
)
// Act.
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/VideoCapabilitiesTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/VideoCapabilitiesTest.kt
index d08276e..29f8235 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/VideoCapabilitiesTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/VideoCapabilitiesTest.kt
@@ -17,11 +17,14 @@
package androidx.camera.video
import android.os.Build
+import android.util.Size
import androidx.camera.testing.CamcorderProfileUtil
import androidx.camera.testing.CamcorderProfileUtil.PROFILE_2160P
import androidx.camera.testing.CamcorderProfileUtil.PROFILE_720P
import androidx.camera.testing.fakes.FakeCamcorderProfileProvider
import androidx.camera.testing.fakes.FakeCameraInfoInternal
+import androidx.core.util.component1
+import androidx.core.util.component2
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,38 +42,78 @@
private val cameraInfo = FakeCameraInfoInternal().apply {
camcorderProfileProvider = FakeCamcorderProfileProvider.Builder()
- .addProfile(PROFILE_HIGH)
- .addProfile(PROFILE_2160P)
- .addProfile(PROFILE_720P)
- .addProfile(PROFILE_LOW)
+ .addProfile(PROFILE_HIGH) // UHD (2160p) per above definition
+ .addProfile(PROFILE_2160P) // UHD (2160p)
+ .addProfile(PROFILE_720P) // HD (720p)
+ .addProfile(PROFILE_LOW) // HD (720p) per above definition
.build()
}
@Test
fun isQualitySupported() {
val videoCapabilities = VideoCapabilities.from(cameraInfo)
- assertThat(videoCapabilities.isQualitySupported(QualitySelector.QUALITY_NONE)).isFalse()
- assertThat(videoCapabilities.isQualitySupported(QualitySelector.QUALITY_HIGHEST)).isTrue()
- assertThat(videoCapabilities.isQualitySupported(QualitySelector.QUALITY_LOWEST)).isTrue()
- assertThat(videoCapabilities.isQualitySupported(QualitySelector.QUALITY_UHD)).isTrue()
- assertThat(videoCapabilities.isQualitySupported(QualitySelector.QUALITY_FHD)).isFalse()
- assertThat(videoCapabilities.isQualitySupported(QualitySelector.QUALITY_HD)).isTrue()
- assertThat(videoCapabilities.isQualitySupported(QualitySelector.QUALITY_SD)).isFalse()
+ assertThat(videoCapabilities.isQualitySupported(Quality.HIGHEST)).isTrue()
+ assertThat(videoCapabilities.isQualitySupported(Quality.LOWEST)).isTrue()
+ assertThat(videoCapabilities.isQualitySupported(Quality.UHD)).isTrue()
+ assertThat(videoCapabilities.isQualitySupported(Quality.FHD)).isFalse()
+ assertThat(videoCapabilities.isQualitySupported(Quality.HD)).isTrue()
+ assertThat(videoCapabilities.isQualitySupported(Quality.SD)).isFalse()
}
@Test
fun getProfile() {
val videoCapabilities = VideoCapabilities.from(cameraInfo)
- assertThat(videoCapabilities.getProfile(QualitySelector.QUALITY_NONE)).isNull()
- assertThat(videoCapabilities.getProfile(QualitySelector.QUALITY_HIGHEST))
+ assertThat(videoCapabilities.getProfile(Quality.HIGHEST))
.isEqualTo(PROFILE_2160P)
- assertThat(videoCapabilities.getProfile(QualitySelector.QUALITY_LOWEST))
+ assertThat(videoCapabilities.getProfile(Quality.LOWEST))
.isEqualTo(PROFILE_720P)
- assertThat(videoCapabilities.getProfile(QualitySelector.QUALITY_UHD))
+ assertThat(videoCapabilities.getProfile(Quality.UHD))
.isEqualTo(PROFILE_2160P)
- assertThat(videoCapabilities.getProfile(QualitySelector.QUALITY_FHD)).isNull()
- assertThat(videoCapabilities.getProfile(QualitySelector.QUALITY_HD))
+ assertThat(videoCapabilities.getProfile(Quality.FHD)).isNull()
+ assertThat(videoCapabilities.getProfile(Quality.HD))
.isEqualTo(PROFILE_720P)
- assertThat(videoCapabilities.getProfile(QualitySelector.QUALITY_SD)).isNull()
+ assertThat(videoCapabilities.getProfile(Quality.SD)).isNull()
+ }
+
+ @Test
+ fun findHighestSupportedQuality_returnsHigherQuality() {
+ val videoCapabilities = VideoCapabilities.from(cameraInfo)
+ // Create a size between 720p and 2160p
+ val (width720p, height720p) = CamcorderProfileUtil.RESOLUTION_720P
+ val inBetweenSize = Size(width720p + 10, height720p)
+
+ assertThat(videoCapabilities.findHighestSupportedQualityFor(inBetweenSize))
+ .isEqualTo(Quality.UHD)
+ }
+
+ @Test
+ fun findHighestSupportedQuality_returnsHighestQuality_whenAboveHighest() {
+ val videoCapabilities = VideoCapabilities.from(cameraInfo)
+ // Create a size between greater than the max quality (UHD)
+ val (width2160p, height2160p) = CamcorderProfileUtil.RESOLUTION_2160P
+ val aboveHighestSize = Size(width2160p + 10, height2160p)
+
+ assertThat(videoCapabilities.findHighestSupportedQualityFor(aboveHighestSize))
+ .isEqualTo(Quality.UHD)
+ }
+
+ @Test
+ fun findHighestSupportedQuality_returnsLowestQuality_whenBelowLowest() {
+ val videoCapabilities = VideoCapabilities.from(cameraInfo)
+ // Create a size below the lowest quality (HD)
+ val (width720p, height720p) = CamcorderProfileUtil.RESOLUTION_720P
+ val belowLowestSize = Size(width720p - 10, height720p)
+
+ assertThat(videoCapabilities.findHighestSupportedQualityFor(belowLowestSize))
+ .isEqualTo(Quality.HD)
+ }
+
+ @Test
+ fun findHighestSupportedQuality_returnsExactQuality_whenExactSizeGiven() {
+ val videoCapabilities = VideoCapabilities.from(cameraInfo)
+ val exactSize720p = CamcorderProfileUtil.RESOLUTION_720P
+
+ assertThat(videoCapabilities.findHighestSupportedQualityFor(exactSize720p))
+ .isEqualTo(Quality.HD)
}
}
diff --git a/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt b/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
index 40cfac4..2b5276c 100644
--- a/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
+++ b/camera/camera-video/src/test/java/androidx/camera/video/VideoCaptureTest.kt
@@ -47,12 +47,6 @@
import androidx.camera.testing.fakes.FakeCameraDeviceSurfaceManager
import androidx.camera.testing.fakes.FakeCameraFactory
import androidx.camera.testing.fakes.FakeCameraInfoInternal
-import androidx.camera.video.QualitySelector.QUALITY_FHD
-import androidx.camera.video.QualitySelector.QUALITY_HD
-import androidx.camera.video.QualitySelector.QUALITY_HIGHEST
-import androidx.camera.video.QualitySelector.QUALITY_LOWEST
-import androidx.camera.video.QualitySelector.QUALITY_SD
-import androidx.camera.video.QualitySelector.QUALITY_UHD
import androidx.camera.video.VideoOutput.StreamState
import androidx.camera.video.impl.VideoCaptureConfig
import androidx.core.util.Consumer
@@ -170,10 +164,10 @@
// Camera 0 support 2160P(UHD) and 720P(HD)
val qualityList = arrayOf(
- QUALITY_UHD to RESOLUTION_2160P,
- QUALITY_HD to RESOLUTION_720P,
- QUALITY_HIGHEST to RESOLUTION_2160P,
- QUALITY_LOWEST to RESOLUTION_720P,
+ Quality.UHD to RESOLUTION_2160P,
+ Quality.HD to RESOLUTION_720P,
+ Quality.HIGHEST to RESOLUTION_2160P,
+ Quality.LOWEST to RESOLUTION_720P,
)
qualityList.forEach { (quality, resolution) ->
surfaceManager.setSuggestedResolution(
@@ -184,7 +178,7 @@
val videoOutput = createVideoOutput(
mediaSpec = MediaSpec.builder().configureVideo {
- it.setQualitySelector(QualitySelector.of(quality))
+ it.setQualitySelector(QualitySelector.from(quality))
}.build()
)
val videoCapture = VideoCapture.Builder(videoOutput)
@@ -223,10 +217,14 @@
val videoOutput = createVideoOutput(
mediaSpec = MediaSpec.builder().configureVideo {
it.setQualitySelector(
- QualitySelector.firstTry(QUALITY_UHD) // 2160P
- .thenTry(QUALITY_SD) // 480P
- .thenTry(QUALITY_HD) // 720P
- .finallyTry(QUALITY_FHD) // 1080P
+ QualitySelector.fromOrderedList(
+ listOf(
+ Quality.UHD, // 2160P
+ Quality.SD, // 480P
+ Quality.HD, // 720P
+ Quality.FHD // 1080P
+ )
+ )
)
}.build()
)
@@ -254,7 +252,7 @@
// Camera 0 support 2160P(UHD) and 720P(HD)
val videoOutput = createVideoOutput(
mediaSpec = MediaSpec.builder().configureVideo {
- it.setQualitySelector(QualitySelector.of(QUALITY_FHD))
+ it.setQualitySelector(QualitySelector.from(Quality.FHD))
}.build()
)
val videoCapture = VideoCapture.Builder(videoOutput)
@@ -277,7 +275,7 @@
val videoOutput = createVideoOutput(
mediaSpec = MediaSpec.builder().configureVideo {
- it.setQualitySelector(QualitySelector.of(QUALITY_UHD))
+ it.setQualitySelector(QualitySelector.from(Quality.UHD))
}.build()
)
val videoCapture = VideoCapture.Builder(videoOutput)
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/Camera2InteropIntegrationTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/Camera2InteropIntegrationTest.kt
index b3286f1..84eccda 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/Camera2InteropIntegrationTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/Camera2InteropIntegrationTest.kt
@@ -21,8 +21,10 @@
import android.content.Context
import android.content.Context.CAMERA_SERVICE
import android.hardware.camera2.CameraCaptureSession
+import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
+import android.hardware.camera2.CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.TotalCaptureResult
import androidx.camera.camera2.interop.Camera2CameraControl
@@ -52,8 +54,8 @@
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
-import kotlinx.coroutines.withTimeout
import org.junit.After
+import org.junit.Assert.assertTrue
import org.junit.Assume
import org.junit.Before
import org.junit.Rule
@@ -127,7 +129,11 @@
fun canUseCameraSelector_fromCamera2CameraIdAndCameraFilter(): Unit = runBlocking {
val camera2CameraManager = ApplicationProvider.getApplicationContext<Context>()
.getSystemService(CAMERA_SERVICE) as CameraManager
- camera2CameraManager.cameraIdList.forEach { id ->
+ camera2CameraManager.cameraIdList.forEach continuing@{ id ->
+ if (!isBackwardCompatible(camera2CameraManager, id)) {
+ return@continuing
+ }
+
val cameraSelector = CameraSelector.Builder()
.addCameraFilter {
it.filter { Camera2CameraInfo.from(it).cameraId == id }
@@ -147,7 +153,10 @@
fun canUseCameraSelector_fromCamera2CameraIdAndAvailableCameraInfos(): Unit = runBlocking {
val camera2CameraManager = ApplicationProvider.getApplicationContext<Context>()
.getSystemService(CAMERA_SERVICE) as CameraManager
- camera2CameraManager.cameraIdList.forEach { id ->
+ camera2CameraManager.cameraIdList.forEach continuing@{ id ->
+ if (!isBackwardCompatible(camera2CameraManager, id)) {
+ return@continuing
+ }
withContext(Dispatchers.Main) {
val cameraSelector =
processCameraProvider!!.availableCameraInfos.find {
@@ -223,17 +232,15 @@
val waitingList = mutableListOf<CaptureContainer>()
- suspend fun waitFor(
+ fun waitFor(
timeout: Long = TimeUnit.SECONDS.toMillis(5),
numOfCaptures: Int = 1,
verifyResults: (captureRequests: List<CaptureRequest>) -> Unit
) {
val resultContainer = CaptureContainer(CountDownLatch(numOfCaptures))
waitingList.add(resultContainer)
- withTimeout(timeout) {
- resultContainer.countDownLatch.await()
- verifyResults(resultContainer.captureRequests)
- }
+ assertTrue(resultContainer.countDownLatch.await(timeout, TimeUnit.MILLISECONDS))
+ verifyResults(resultContainer.captureRequests)
waitingList.remove(resultContainer)
}
@@ -288,4 +295,16 @@
}
setDeviceStateCallback(stateCallback)
}.asStateFlow()
+
+ private fun isBackwardCompatible(cameraManager: CameraManager, cameraId: String): Boolean {
+ val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId)
+ val capabilities =
+ cameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
+
+ capabilities?.let {
+ return it.contains(REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)
+ }
+
+ return false
+ }
}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt
index 05646dc..5b915ad 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageAnalysisTest.kt
@@ -247,12 +247,12 @@
.setBackpressureStrategy(strategy)
.build()
withContext(Dispatchers.Main) {
- cameraProvider.bindToLifecycle(fakeLifecycleOwner, DEFAULT_CAMERA_SELECTOR, useCase)
+ cameraProvider.bindToLifecycle(fakeLifecycleOwner, DEFAULT_CAMERA_SELECTOR, useCase)
+ }
+ useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(handler), analyzer)
+ analysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS)
+ synchronized(analysisResultLock) { assertThat(analysisResults).isNotEmpty() }
}
- useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(handler), analyzer)
- analysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS)
- synchronized(analysisResultLock) { assertThat(analysisResults).isNotEmpty() }
- }
@Test
fun analyzerDoesNotAnalyzeImages_whenCameraIsNotOpen() = runBlocking {
@@ -351,41 +351,6 @@
assertThat(newConfig.targetResolution == expectedTargetResolution).isTrue()
}
- // TODO(b/162298517): change the test to be deterministic instead of depend upon timing.
- @Test
- fun analyzerSetMultipleTimesInKeepOnlyLatestMode() = runBlocking {
- val useCase = ImageAnalysis.Builder()
- .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
- .build()
- withContext(Dispatchers.Main) {
- cameraProvider.bindToLifecycle(fakeLifecycleOwner, DEFAULT_CAMERA_SELECTOR, useCase)
- }
- useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(handler), analyzer)
- analysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS)
-
- val slowAnalyzer = ImageAnalysis.Analyzer { image ->
- try {
- Thread.sleep(200)
- image.close()
- } catch (e: Exception) {
- }
- }
- useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(handler), slowAnalyzer)
- Thread.sleep(100)
-
- useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(handler), slowAnalyzer)
- Thread.sleep(100)
-
- useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(handler), slowAnalyzer)
- Thread.sleep(100)
-
- useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(handler), slowAnalyzer)
- Thread.sleep(100)
-
- useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(handler), slowAnalyzer)
- Thread.sleep(100)
- }
-
@Test
fun useCaseConfigCanBeReset_afterUnbind() = runBlocking {
val useCase = ImageAnalysis.Builder().build()
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index 795a095..15baf68 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -19,11 +19,6 @@
import static androidx.camera.core.ImageCapture.FLASH_MODE_AUTO;
import static androidx.camera.core.ImageCapture.FLASH_MODE_OFF;
import static androidx.camera.core.ImageCapture.FLASH_MODE_ON;
-import static androidx.camera.video.QualitySelector.QUALITY_FHD;
-import static androidx.camera.video.QualitySelector.QUALITY_HD;
-import static androidx.camera.video.QualitySelector.QUALITY_NONE;
-import static androidx.camera.video.QualitySelector.QUALITY_SD;
-import static androidx.camera.video.QualitySelector.QUALITY_UHD;
import static androidx.camera.video.VideoRecordEvent.Finalize.ERROR_FILE_SIZE_LIMIT_REACHED;
import static androidx.camera.video.VideoRecordEvent.Finalize.ERROR_INSUFFICIENT_STORAGE;
import static androidx.camera.video.VideoRecordEvent.Finalize.ERROR_NONE;
@@ -94,6 +89,7 @@
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.video.MediaStoreOutputOptions;
import androidx.camera.video.OutputOptions;
+import androidx.camera.video.Quality;
import androidx.camera.video.QualitySelector;
import androidx.camera.video.Recorder;
import androidx.camera.video.Recording;
@@ -156,6 +152,7 @@
private final AtomicLong mPreviewFrameCount = new AtomicLong(0);
private final MutableLiveData<String> mImageAnalysisResult = new MutableLiveData<>();
private static final String BACKWARD = "BACKWARD";
+ private static final Quality QUALITY_AUTO = null;
private Recording mActiveRecording;
/** The camera to use */
@@ -192,7 +189,7 @@
private OpenGLRenderer mPreviewRenderer;
private DisplayManager.DisplayListener mDisplayListener;
private RecordUi mRecordUi;
- private int mVideoQuality = QUALITY_NONE;
+ private Quality mVideoQuality;
SessionImagesUriSet mSessionImagesUriSet = new SessionImagesUriSet();
@@ -423,21 +420,22 @@
// Add Auto item
final int groupId = Menu.NONE;
final int autoOrder = 0;
- menu.add(groupId, QUALITY_NONE, autoOrder, getQualityMenuItemName(QUALITY_NONE));
- if (mVideoQuality == QUALITY_NONE) {
- menu.findItem(QUALITY_NONE).setChecked(true);
+ final int autoMenuId = qualityToItemId(QUALITY_AUTO);
+ menu.add(groupId, autoMenuId, autoOrder, getQualityMenuItemName(QUALITY_AUTO));
+ if (mVideoQuality == QUALITY_AUTO) {
+ menu.findItem(autoMenuId).setChecked(true);
}
// Add device supported qualities
- List<Integer> supportedQualities =
+ List<Quality> supportedQualities =
QualitySelector.getSupportedQualities(mCamera.getCameraInfo());
// supportedQualities has been sorted by descending order.
for (int i = 0; i < supportedQualities.size(); i++) {
- int quality = supportedQualities.get(i);
- menu.add(groupId, quality, autoOrder + 1 + i,
- getQualityMenuItemName(quality));
+ Quality quality = supportedQualities.get(i);
+ int itemId = qualityToItemId(quality);
+ menu.add(groupId, itemId, autoOrder + 1 + i, getQualityMenuItemName(quality));
if (mVideoQuality == quality) {
- menu.findItem(quality).setChecked(true);
+ menu.findItem(itemId).setChecked(true);
}
}
@@ -445,8 +443,9 @@
menu.setGroupCheckable(groupId, true, true);
popup.setOnMenuItemClickListener(item -> {
- if (item.getItemId() != mVideoQuality) {
- mVideoQuality = item.getItemId();
+ Quality quality = itemIdToQuality(item.getItemId());
+ if (quality != mVideoQuality) {
+ mVideoQuality = quality;
mRecordUi.getButtonQuality().setText(getQualityIconName(mVideoQuality));
// Quality changed, rebind UseCases
tryBindUseCases();
@@ -877,7 +876,7 @@
mUseCases = useCases;
} catch (IllegalArgumentException ex) {
String msg;
- if (mVideoQuality != QUALITY_NONE) {
+ if (mVideoQuality != QUALITY_AUTO) {
msg = "Bind too many use cases or video quality is too large.";
} else {
msg = "Bind too many use cases.";
@@ -891,7 +890,7 @@
mAnalysisToggle.setChecked(getImageAnalysis() != null);
mVideoToggle.setChecked(getVideoCapture() != null);
// Reset video quality to avoid always fail by quality too large.
- mRecordUi.getButtonQuality().setText(getQualityIconName(mVideoQuality = QUALITY_NONE));
+ mRecordUi.getButtonQuality().setText(getQualityIconName(mVideoQuality = QUALITY_AUTO));
if (!calledBySelf) {
// Only call self if not already calling self to avoid an infinite loop.
@@ -942,8 +941,8 @@
if (mVideoToggle.isChecked()) {
Recorder.Builder builder = new Recorder.Builder();
- if (mVideoQuality != QUALITY_NONE) {
- builder.setQualitySelector(QualitySelector.of(mVideoQuality));
+ if (mVideoQuality != QUALITY_AUTO) {
+ builder.setQualitySelector(QualitySelector.from(mVideoQuality));
}
VideoCapture<Recorder> videoCapture = VideoCapture.withOutput(builder.build());
useCases.add(videoCapture);
@@ -1401,38 +1400,68 @@
}
@NonNull
- private static String getQualityIconName(int quality) {
- switch (quality) {
- case QUALITY_NONE:
- return "Auto";
- case QUALITY_UHD:
- return "UHD";
- case QUALITY_FHD:
- return "FHD";
- case QUALITY_HD:
- return "HD";
- case QUALITY_SD:
- return "SD";
- default:
- return "?";
+ private static String getQualityIconName(@Nullable Quality quality) {
+ if (quality == QUALITY_AUTO) {
+ return "Auto";
+ } else if (quality == Quality.UHD) {
+ return "UHD";
+ } else if (quality == Quality.FHD) {
+ return "FHD";
+ } else if (quality == Quality.HD) {
+ return "HD";
+ } else if (quality == Quality.SD) {
+ return "SD";
}
+ return "?";
}
@NonNull
- private static String getQualityMenuItemName(int quality) {
- switch (quality) {
- case QUALITY_NONE:
- return "Auto";
- case QUALITY_UHD:
- return "UHD (2160P)";
- case QUALITY_FHD:
- return "FHD (1080P)";
- case QUALITY_HD:
- return "HD (720P)";
- case QUALITY_SD:
- return "SD (480P)";
+ private static String getQualityMenuItemName(@Nullable Quality quality) {
+ if (quality == QUALITY_AUTO) {
+ return "Auto";
+ } else if (quality == Quality.UHD) {
+ return "UHD (2160P)";
+ } else if (quality == Quality.FHD) {
+ return "FHD (1080P)";
+ } else if (quality == Quality.HD) {
+ return "HD (720P)";
+ } else if (quality == Quality.SD) {
+ return "SD (480P)";
+ }
+ return "Unknown quality";
+ }
+
+ private static int qualityToItemId(@Nullable Quality quality) {
+ if (quality == QUALITY_AUTO) {
+ return 0;
+ } else if (quality == Quality.UHD) {
+ return 1;
+ } else if (quality == Quality.FHD) {
+ return 2;
+ } else if (quality == Quality.HD) {
+ return 3;
+ } else if (quality == Quality.SD) {
+ return 4;
+ } else {
+ throw new IllegalArgumentException("Undefined quality: " + quality);
+ }
+ }
+
+ @Nullable
+ private static Quality itemIdToQuality(int itemId) {
+ switch (itemId) {
+ case 0:
+ return QUALITY_AUTO;
+ case 1:
+ return Quality.UHD;
+ case 2:
+ return Quality.FHD;
+ case 3:
+ return Quality.HD;
+ case 4:
+ return Quality.SD;
default:
- return "Unknown quality";
+ throw new IllegalArgumentException("Undefined item id: " + itemId);
}
}
}
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java
index 55deed1..8bb3a03 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java
@@ -224,9 +224,9 @@
assertThat(tollCard.getCardState().getValue()).isEqualTo(TollCard.TOLLCARD_STATE_VALID);
}
- @Config(minSdk = 30)
+ @Config(maxSdk = 30)
@Test
- public void getTollCard_verifyResponseApi30() {
+ public void getTollCard_verifyResponseApi30() throws InterruptedException {
AtomicReference<TollCard> loadedResult = new AtomicReference<>();
OnCarDataAvailableListener<TollCard> listener = (data) -> {
loadedResult.set(data);
diff --git a/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml b/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml
index a6c75ae..b199784 100644
--- a/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/navigation/automotive/src/main/AndroidManifest.xml
@@ -86,6 +86,11 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
+ <intent-filter>
+ <action android:name="androidx.car.app.action.NAVIGATE" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="geo" />
+ </intent-filter>
<meta-data android:name="distractionOptimized" android:value="true"/>
</activity>
diff --git a/car/app/app-samples/navigation/mobile/src/main/AndroidManifest.xml b/car/app/app-samples/navigation/mobile/src/main/AndroidManifest.xml
index 99e5e3d..1f0438d 100644
--- a/car/app/app-samples/navigation/mobile/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/navigation/mobile/src/main/AndroidManifest.xml
@@ -60,6 +60,11 @@
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION"/>
</intent-filter>
+ <intent-filter>
+ <action android:name="androidx.car.app.action.NAVIGATE" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="geo" />
+ </intent-filter>
</service>
<service
android:name="androidx.car.app.sample.navigation.common.nav.NavigationService"
diff --git a/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml b/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
index 8c6a8d6..e15984a 100644
--- a/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
@@ -127,6 +127,12 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
+
+ <intent-filter>
+ <action android:name="androidx.car.app.action.NAVIGATE" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="geo" />
+ </intent-filter>
</activity>
</application>
diff --git a/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml b/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
index 53e5dc9..54b62dca 100644
--- a/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
+++ b/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
@@ -134,6 +134,11 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
+ <intent-filter>
+ <action android:name="androidx.car.app.action.NAVIGATE" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="geo" />
+ </intent-filter>
</activity>
</application>
diff --git a/car/app/app-samples/showcase/common/build.gradle b/car/app/app-samples/showcase/common/build.gradle
index 830549e..c976cdc 100644
--- a/car/app/app-samples/showcase/common/build.gradle
+++ b/car/app/app-samples/showcase/common/build.gradle
@@ -29,7 +29,7 @@
implementation(project(":car:app:app"))
api(libs.kotlinStdlib) // Due to :annotation-experimental
- implementation("androidx.core:core:1.6.0-alpha01")
+ implementation("androidx.core:core:1.7.0")
implementation(project(":annotation:annotation-experimental"))
}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java
index 2362466..ad6e272 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java
@@ -18,8 +18,12 @@
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.car.app.CarAppPermission;
@@ -33,6 +37,7 @@
import androidx.car.app.model.OnClickListener;
import androidx.car.app.model.ParkedOnlyOnClickListener;
import androidx.car.app.model.Template;
+import androidx.core.location.LocationManagerCompat;
import java.util.ArrayList;
import java.util.List;
@@ -131,10 +136,9 @@
getCarContext(),
String.format("Approved: %s Rejected: %s", approved, rejected),
CarToast.LENGTH_LONG).show();
- finish();
});
if (!getCarContext().getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
- CarToast.makeText(getCarContext(), "Grant Permission on the phone",
+ CarToast.makeText(getCarContext(), "Grant Permission on the phone screen",
CarToast.LENGTH_LONG).show();
}
});
@@ -145,10 +149,39 @@
.setOnClickListener(listener)
.build();
- return new LongMessageTemplate.Builder(message)
+
+ Action action2 = null;
+ LocationManager locationManager =
+ (LocationManager) getCarContext().getSystemService(Context.LOCATION_SERVICE);
+ if (!LocationManagerCompat.isLocationEnabled(locationManager)) {
+ message.append("Enable Location Permissions on device\n");
+ action2 = new Action.Builder()
+ .setTitle("Enable Location")
+ .setBackgroundColor(CarColor.BLUE)
+ .setOnClickListener(ParkedOnlyOnClickListener.create(() -> {
+ getCarContext().startActivity(
+ new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS).addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK));
+ if (!getCarContext().getPackageManager().hasSystemFeature(
+ FEATURE_AUTOMOTIVE)) {
+ CarToast.makeText(getCarContext(), "Enable location on the phone "
+ + "screen",
+ CarToast.LENGTH_LONG).show();
+ }
+ }))
+ .build();
+ }
+
+
+ LongMessageTemplate.Builder builder = new LongMessageTemplate.Builder(message)
.setTitle("Required Permissions")
.addAction(action)
- .setHeaderAction(headerAction)
- .build();
+ .setHeaderAction(headerAction);
+
+ if (action2 != null) {
+ builder.addAction(action2);
+ }
+
+ return builder.build();
}
}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SearchTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SearchTemplateDemoScreen.java
index 27679a9..6f5a9e1 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SearchTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SearchTemplateDemoScreen.java
@@ -17,7 +17,6 @@
package androidx.car.app.sample.showcase.common.templates;
import static androidx.car.app.CarToast.LENGTH_LONG;
-import static androidx.car.app.CarToast.LENGTH_SHORT;
import androidx.annotation.NonNull;
import androidx.car.app.CarContext;
@@ -55,20 +54,10 @@
new SearchCallback() {
@Override
public void onSearchTextChanged(@NonNull String searchText) {
- CarToast.makeText(
- getCarContext(),
- "Search changed: " + searchText,
- LENGTH_SHORT)
- .show();
}
@Override
public void onSearchSubmitted(@NonNull String searchText) {
- CarToast.makeText(
- getCarContext(),
- "Search submitted: " + searchText,
- LENGTH_LONG)
- .show();
}
};
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
index ebdf4ab1..d5a23ef 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
@@ -69,7 +69,7 @@
() -> getScreenManager().push(new LongMessageTemplateDemoScreen(getCarContext())));
private final Action mProviderSignInAction = new Action.Builder()
- .setTitle("Google Sign-In")
+ .setTitle("Google sign-in")
.setOnClickListener(ParkedOnlyOnClickListener.create(() -> {
mState = State.PROVIDER;
invalidate();
diff --git a/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml b/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml
index 519d9ef..9972a0f 100644
--- a/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml
@@ -65,6 +65,11 @@
<action android:name="androidx.car.app.CarAppService"/>
<category android:name="androidx.car.app.category.NAVIGATION"/>
</intent-filter>
+ <intent-filter>
+ <action android:name="androidx.car.app.action.NAVIGATE" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="geo" />
+ </intent-filter>
</service>
<service
diff --git a/car/app/app-samples/showcase/mobile/src/main/AndroidManifestWithSdkVersion.xml b/car/app/app-samples/showcase/mobile/src/main/AndroidManifestWithSdkVersion.xml
index 9f6d52a..000a00c 100644
--- a/car/app/app-samples/showcase/mobile/src/main/AndroidManifestWithSdkVersion.xml
+++ b/car/app/app-samples/showcase/mobile/src/main/AndroidManifestWithSdkVersion.xml
@@ -72,6 +72,11 @@
<action android:name="androidx.car.app.CarAppService"/>
<category android:name="androidx.car.app.category.NAVIGATION"/>
</intent-filter>
+ <intent-filter>
+ <action android:name="androidx.car.app.action.NAVIGATE" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="geo" />
+ </intent-filter>
</service>
<service
diff --git a/car/app/app/src/main/java/androidx/car/app/CarAppPermissionActivity.java b/car/app/app/src/main/java/androidx/car/app/CarAppPermissionActivity.java
index aa165cf..664f64a 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarAppPermissionActivity.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarAppPermissionActivity.java
@@ -137,8 +137,8 @@
listener.onRequestPermissionsResult(granted.toArray(new String[0]),
rejected.toArray(new String[0]));
} catch (RemoteException e) {
- // This is impossible since it is running in the same process.
- throw new IllegalStateException(e);
+ Log.e(LogTags.TAG, "CarAppService dead when accepting/rejecting "
+ + "permissions", e);
}
finish();
diff --git a/collection2/collection2/src/commonMain/kotlin/androidx/collection/ArraySet.kt b/collection2/collection2/src/commonMain/kotlin/androidx/collection/ArraySet.kt
index cada5c3..d17909c 100644
--- a/collection2/collection2/src/commonMain/kotlin/androidx/collection/ArraySet.kt
+++ b/collection2/collection2/src/commonMain/kotlin/androidx/collection/ArraySet.kt
@@ -18,16 +18,46 @@
import kotlin.jvm.JvmOverloads
+/**
+ * ArraySet is a generic set data structure that is designed to be more memory efficient than a
+ * traditional [HashSet]. The design is very similar to `ArrayMap`, with all of the caveats
+ * described there. This implementation is separate from `ArrayMap`, however, so the array
+ * contains only one item for each entry in the set (instead of a pair for a mapping).
+ *
+ * Note that this implementation is not intended to be appropriate for data structures
+ * that may contain large numbers of items. It is generally slower than a traditional
+ * [HashSet], since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array. For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%.
+ *
+ * Because this container is intended to better balance memory use, unlike most other
+ * standard Java containers it will shrink its array as items are removed from it. Currently
+ * you have no control over this shrinking -- if you set a capacity and then remove an
+ * item, it may reduce the capacity to better match the current size. In the future an
+ * explicit call to set the capacity should turn off this aggressive shrinking behavior.
+ *
+ * This structure is *NOT* thread-safe.
+ *
+ * @param capacity initial capacity of the set. The default capacity of an array set is 0, and it
+ * will grow once items are added to it.
+ */
class ArraySet<E>
@JvmOverloads constructor(
capacity: Int = 0
) : MutableCollection<E>, MutableSet<E> {
+
+ /**
+ * Create a new ArraySet with the elements from the given ArraySet.
+ */
constructor(set: ArraySet<out E>?) : this(0) {
if (set != null) {
addAll(set)
}
}
+ /**
+ * Create a new ArraySet with the elements from the given [Collection].
+ */
constructor(set: Collection<E>?) : this(0) {
if (set != null) {
addAll(set)
@@ -50,12 +80,20 @@
private var _size: Int = 0
+ /**
+ * The number of items in this set.
+ */
@Suppress("NON_FINAL_MEMBER_IN_FINAL_CLASS") // For japicmp.
open override val size: Int get() = _size
+ /**
+ * Return `true` if the set contains no items.
+ */
override fun isEmpty(): Boolean = _size == 0
/**
+ * Ensure the set can hold at least [minimumCapacity] items.
+ *
* @throws ConcurrentModificationException if the set has been concurrently modified.
*/
@Suppress("NON_FINAL_MEMBER_IN_FINAL_CLASS") // For japicmp.
@@ -70,6 +108,12 @@
}
}
+ /**
+ * Returns the index of [element] in the set.
+ *
+ * @param element The value to search for.
+ * @return the index of [element] if it exists, else a negative integer.
+ */
@Suppress("NON_FINAL_MEMBER_IN_FINAL_CLASS") // For japicmp.
open fun indexOf(element: E): Int {
return if (element == null) {
@@ -132,10 +176,21 @@
return end.inv()
}
+ /**
+ * Return the value at the given index in the array.
+ *
+ * @param index The desired index, must be between 0 and [size]-1.
+ * @return the value stored at [index].
+ */
@Suppress("NON_FINAL_MEMBER_IN_FINAL_CLASS") // For japicmp.
open fun valueAt(index: Int): E? = values[index]
/**
+ * Adds the specified [element] to this set. The set is not modified if it
+ * already contains [element].
+ *
+ * @param element the object to add.
+ * @return `true` if this set is modified, `false` otherwise.
* @throws ConcurrentModificationException if the set has been concurrently modified.
*/
override fun add(element: E): Boolean {
@@ -194,6 +249,10 @@
return true
}
+ /**
+ * [add] all values in [elements]
+ * @param elements The collection whose contents are to be added.
+ */
override fun addAll(elements: Collection<E>): Boolean {
ensureCapacity(_size + elements.size)
var added = false
@@ -203,6 +262,10 @@
return added
}
+ /**
+ * [add] all values in [elements]
+ * @param elements The array whose contents are to be added.
+ */
@Suppress("NON_FINAL_MEMBER_IN_FINAL_CLASS") // For japicmp.
open fun addAll(elements: ArraySet<E>) {
val arraySize = elements._size
@@ -219,6 +282,12 @@
}
}
+ /**
+ * Removes the specified [element] from this set.
+ *
+ * @param element the object to remove.
+ * @return `true` if this set is modified, `false` otherwise.
+ */
override fun remove(element: E): Boolean {
val index = indexOf(element)
if (index >= 0) {
@@ -229,6 +298,10 @@
}
/**
+ * Remove the element at the given [index].
+ * @param index The desired index, must be between 0 and [size]-1.
+ * @return Returns the value that was stored at this [index].
+ *
* @throws ConcurrentModificationException if the set has been concurrently modified.
*/
@Suppress("NON_FINAL_MEMBER_IN_FINAL_CLASS") // For japicmp.
@@ -304,6 +377,10 @@
return value
}
+ /**
+ * [remove] all values in [elements]
+ * @return `true` if any values were removed from the array set, else `false`.
+ */
override fun removeAll(elements: Collection<E>): Boolean {
var removed = false
for (element in elements) {
@@ -312,6 +389,10 @@
return removed
}
+ /**
+ * [remove] all values in [elements]
+ * @return `true` if any values were removed from the array set, else `false`.
+ */
@Suppress("NON_FINAL_MEMBER_IN_FINAL_CLASS") // For japicmp.
open fun removeAll(elements: ArraySet<out E>): Boolean {
// TODO: If array is sufficiently large, a marking approach might be beneficial. In a first
@@ -327,6 +408,9 @@
return originalSize != _size
}
+ /**
+ * Make the set empty. All storage is released.
+ */
override fun clear() {
hashes = EMPTY_INTS
@Suppress("UNCHECKED_CAST") // Empty array.
@@ -334,6 +418,12 @@
_size = 0
}
+ /**
+ * Remove all values in the array set that do *not* exist in the given [elements].
+ * @param elements The collection whose contents are to be used to determine which
+ * values to keep.
+ * @return `true` if any values were removed from the array set, else `false`.
+ */
override fun retainAll(elements: Collection<E>): Boolean {
var removed = false
for (i in 0 until _size) {
@@ -345,12 +435,31 @@
return removed
}
+ /**
+ * Check whether a value exists in the set.
+ *
+ * @param element The value to search for.
+ * @return whether [element] exists in the set.
+ */
override operator fun contains(element: E): Boolean = indexOf(element) >= 0
+ /**
+ * Determine if the array set contains all of the values in the given collection.
+ *
+ * @param elements The collection whose contents are to be checked against.
+ * @return `true` if this array set contains a value for every entry
+ * in [elements], else `false`.
+ */
override fun containsAll(elements: Collection<E>): Boolean {
return elements.all { it in this }
}
+ /**
+ * Return an [MutableIterator] over all values in the set.
+ *
+ * *Note:* this is a less efficient way to access the array contents compared to
+ * looping from 0 until [size] and calling [valueAt].
+ */
override fun iterator(): MutableIterator<E> {
return Iterator(_size)
}
@@ -365,6 +474,10 @@
}
private companion object {
+ /**
+ * The minimum amount by which the capacity of a [ArraySet] will increase.
+ * This is tuned to be relatively space-efficient.
+ */
private const val BASE_SIZE = 4
private const val BASE_SIZE_2X = BASE_SIZE * 2
}
diff --git a/compose/animation/animation-graphics/api/1.1.0-beta04.txt b/compose/animation/animation-graphics/api/1.1.0-beta04.txt
index 5522e93..5f573e31 100644
--- a/compose/animation/animation-graphics/api/1.1.0-beta04.txt
+++ b/compose/animation/animation-graphics/api/1.1.0-beta04.txt
@@ -1,6 +1,9 @@
// Signature format: 4.0
package androidx.compose.animation.graphics.res {
+ public final class AnimatedVectorPainterResources_androidKt {
+ }
+
public final class AnimatedVectorResources_androidKt {
}
diff --git a/compose/animation/animation-graphics/api/current.txt b/compose/animation/animation-graphics/api/current.txt
index 5522e93..5f573e31 100644
--- a/compose/animation/animation-graphics/api/current.txt
+++ b/compose/animation/animation-graphics/api/current.txt
@@ -1,6 +1,9 @@
// Signature format: 4.0
package androidx.compose.animation.graphics.res {
+ public final class AnimatedVectorPainterResources_androidKt {
+ }
+
public final class AnimatedVectorResources_androidKt {
}
diff --git a/compose/animation/animation-graphics/api/public_plus_experimental_1.1.0-beta04.txt b/compose/animation/animation-graphics/api/public_plus_experimental_1.1.0-beta04.txt
index 7062c06..91d3cf3 100644
--- a/compose/animation/animation-graphics/api/public_plus_experimental_1.1.0-beta04.txt
+++ b/compose/animation/animation-graphics/api/public_plus_experimental_1.1.0-beta04.txt
@@ -8,8 +8,12 @@
package androidx.compose.animation.graphics.res {
+ public final class AnimatedVectorPainterResources_androidKt {
+ method @androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.painter.Painter rememberAnimatedVectorPainter(androidx.compose.animation.graphics.vector.AnimatedImageVector animatedImageVector, boolean atEnd);
+ }
+
public final class AnimatedVectorResources_androidKt {
- method @androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi @androidx.compose.runtime.Composable public static androidx.compose.animation.graphics.vector.AnimatedImageVector animatedVectorResource(@DrawableRes int id);
+ method @androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi @androidx.compose.runtime.Composable public static androidx.compose.animation.graphics.vector.AnimatedImageVector animatedVectorResource(androidx.compose.animation.graphics.vector.AnimatedImageVector.Companion, @DrawableRes int id);
}
public final class AnimatorResources_androidKt {
@@ -22,9 +26,12 @@
@androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi @androidx.compose.runtime.Immutable public final class AnimatedImageVector {
method public androidx.compose.ui.graphics.vector.ImageVector getImageVector();
method public int getTotalDuration();
- method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter painterFor(boolean atEnd);
property public final androidx.compose.ui.graphics.vector.ImageVector imageVector;
property public final int totalDuration;
+ field public static final androidx.compose.animation.graphics.vector.AnimatedImageVector.Companion Companion;
+ }
+
+ public static final class AnimatedImageVector.Companion {
}
public final class AnimatorKt {
diff --git a/compose/animation/animation-graphics/api/public_plus_experimental_current.txt b/compose/animation/animation-graphics/api/public_plus_experimental_current.txt
index 7062c06..91d3cf3 100644
--- a/compose/animation/animation-graphics/api/public_plus_experimental_current.txt
+++ b/compose/animation/animation-graphics/api/public_plus_experimental_current.txt
@@ -8,8 +8,12 @@
package androidx.compose.animation.graphics.res {
+ public final class AnimatedVectorPainterResources_androidKt {
+ method @androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.painter.Painter rememberAnimatedVectorPainter(androidx.compose.animation.graphics.vector.AnimatedImageVector animatedImageVector, boolean atEnd);
+ }
+
public final class AnimatedVectorResources_androidKt {
- method @androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi @androidx.compose.runtime.Composable public static androidx.compose.animation.graphics.vector.AnimatedImageVector animatedVectorResource(@DrawableRes int id);
+ method @androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi @androidx.compose.runtime.Composable public static androidx.compose.animation.graphics.vector.AnimatedImageVector animatedVectorResource(androidx.compose.animation.graphics.vector.AnimatedImageVector.Companion, @DrawableRes int id);
}
public final class AnimatorResources_androidKt {
@@ -22,9 +26,12 @@
@androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi @androidx.compose.runtime.Immutable public final class AnimatedImageVector {
method public androidx.compose.ui.graphics.vector.ImageVector getImageVector();
method public int getTotalDuration();
- method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter painterFor(boolean atEnd);
property public final androidx.compose.ui.graphics.vector.ImageVector imageVector;
property public final int totalDuration;
+ field public static final androidx.compose.animation.graphics.vector.AnimatedImageVector.Companion Companion;
+ }
+
+ public static final class AnimatedImageVector.Companion {
}
public final class AnimatorKt {
diff --git a/compose/animation/animation-graphics/api/restricted_1.1.0-beta04.txt b/compose/animation/animation-graphics/api/restricted_1.1.0-beta04.txt
index 5522e93..5f573e31 100644
--- a/compose/animation/animation-graphics/api/restricted_1.1.0-beta04.txt
+++ b/compose/animation/animation-graphics/api/restricted_1.1.0-beta04.txt
@@ -1,6 +1,9 @@
// Signature format: 4.0
package androidx.compose.animation.graphics.res {
+ public final class AnimatedVectorPainterResources_androidKt {
+ }
+
public final class AnimatedVectorResources_androidKt {
}
diff --git a/compose/animation/animation-graphics/api/restricted_current.txt b/compose/animation/animation-graphics/api/restricted_current.txt
index 5522e93..5f573e31 100644
--- a/compose/animation/animation-graphics/api/restricted_current.txt
+++ b/compose/animation/animation-graphics/api/restricted_current.txt
@@ -1,6 +1,9 @@
// Signature format: 4.0
package androidx.compose.animation.graphics.res {
+ public final class AnimatedVectorPainterResources_androidKt {
+ }
+
public final class AnimatedVectorResources_androidKt {
}
diff --git a/compose/animation/animation-graphics/samples/src/main/java/androidx/compose/animation/graphics/samples/AnimatedVectorSample.kt b/compose/animation/animation-graphics/samples/src/main/java/androidx/compose/animation/graphics/samples/AnimatedVectorSample.kt
index e660d5b..722c4b2 100644
--- a/compose/animation/animation-graphics/samples/src/main/java/androidx/compose/animation/graphics/samples/AnimatedVectorSample.kt
+++ b/compose/animation/animation-graphics/samples/src/main/java/androidx/compose/animation/graphics/samples/AnimatedVectorSample.kt
@@ -20,6 +20,8 @@
import androidx.annotation.Sampled
import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.size
@@ -38,10 +40,10 @@
@OptIn(ExperimentalAnimationGraphicsApi::class)
@Composable
fun AnimatedVector(@DrawableRes drawableId: Int) {
- val image = animatedVectorResource(drawableId)
+ val image = AnimatedImageVector.animatedVectorResource(drawableId)
var atEnd by remember { mutableStateOf(false) }
Image(
- painter = image.painterFor(atEnd),
+ painter = rememberAnimatedVectorPainter(image, atEnd),
contentDescription = "Your content description",
modifier = Modifier.size(64.dp).clickable {
atEnd = !atEnd
diff --git a/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt b/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt
new file mode 100644
index 0000000..68c3d6c
--- /dev/null
+++ b/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorPainterResources.android.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.compose.animation.graphics.res
+
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.graphics.vector.RenderVectorGroup
+import androidx.compose.ui.graphics.vector.VectorConfig
+import androidx.compose.ui.graphics.vector.VectorGroup
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+
+/**
+ * Creates and remembers a [Painter] to render an [AnimatedImageVector]. It renders the image
+ * either at the start or the end of all the animations depending on the [atEnd]. Changes to
+ * [atEnd] are animated.
+ *
+ * @param atEnd Whether the animated vector should be rendered at the end of all its animations.
+ *
+ * @sample androidx.compose.animation.graphics.samples.AnimatedVectorSample
+ */
+@ExperimentalAnimationGraphicsApi
+@Composable
+fun rememberAnimatedVectorPainter(
+ animatedImageVector: AnimatedImageVector,
+ atEnd: Boolean
+): Painter {
+ return rememberAnimatedVectorPainter(animatedImageVector, atEnd) { group, overrides ->
+ RenderVectorGroup(group, overrides)
+ }
+}
+
+@ExperimentalAnimationGraphicsApi
+@Composable
+private fun rememberAnimatedVectorPainter(
+ animatedImageVector: AnimatedImageVector,
+ atEnd: Boolean,
+ render: @Composable (VectorGroup, Map<String, VectorConfig>) -> Unit
+): Painter {
+ return rememberVectorPainter(
+ defaultWidth = animatedImageVector.imageVector.defaultWidth,
+ defaultHeight = animatedImageVector.imageVector.defaultHeight,
+ viewportWidth = animatedImageVector.imageVector.viewportWidth,
+ viewportHeight = animatedImageVector.imageVector.viewportHeight,
+ name = animatedImageVector.imageVector.name,
+ tintColor = animatedImageVector.imageVector.tintColor,
+ tintBlendMode = animatedImageVector.imageVector.tintBlendMode,
+ ) { _, _ ->
+ val transition = updateTransition(atEnd, label = animatedImageVector.imageVector.name)
+ render(
+ animatedImageVector.imageVector.root,
+ animatedImageVector.targets.associate { target ->
+ target.name to target.animator.createVectorConfig(
+ transition,
+ animatedImageVector.totalDuration
+ )
+ }
+ )
+ }
+}
diff --git a/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorResources.android.kt b/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorResources.android.kt
index b7f979c..64f61c3 100644
--- a/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorResources.android.kt
+++ b/compose/animation/animation-graphics/src/androidMain/kotlin/androidx/compose/animation/graphics/res/AnimatedVectorResources.android.kt
@@ -20,19 +20,17 @@
import android.util.Xml
import androidx.annotation.DrawableRes
import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.animation.graphics.vector.compat.parseAnimatedImageVector
import androidx.compose.animation.graphics.vector.compat.seekToStartTag
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import org.xmlpull.v1.XmlPullParserException
/**
* Load an [AnimatedImageVector] from an Android resource id.
*
- * Note: This API is transient and will be likely removed for encouraging async resource loading.
- *
* @param id the resource identifier
* @return an animated vector drawable resource.
*
@@ -40,7 +38,9 @@
*/
@ExperimentalAnimationGraphicsApi
@Composable
-fun animatedVectorResource(@DrawableRes id: Int): AnimatedImageVector {
+fun AnimatedImageVector.Companion.animatedVectorResource(
+ @DrawableRes id: Int
+): AnimatedImageVector {
val context = LocalContext.current
val res = context.resources
val theme = context.theme
diff --git a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/AnimatedImageVector.kt b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/AnimatedImageVector.kt
index 57d7abdd..4567ab8 100644
--- a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/AnimatedImageVector.kt
+++ b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/AnimatedImageVector.kt
@@ -16,22 +16,15 @@
package androidx.compose.animation.graphics.vector
-import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
-import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
-import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.graphics.vector.RenderVectorGroup
-import androidx.compose.ui.graphics.vector.VectorGroup
-import androidx.compose.ui.graphics.vector.VectorConfig
-import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.util.fastMaxBy
/**
* Animated vector graphics object that is generated as a result of
* [androidx.compose.animation.graphics.res.loadAnimatedVectorResource].
- * It can be composed and rendered by [painterFor].
+ * It can be composed and rendered by `rememberAnimatedVectorPainter`.
*
* @param imageVector The [ImageVector] to be animated. This is represented with the
* `android:drawable` parameter of an `<animated-vector>` element.
@@ -54,44 +47,9 @@
}?.animator?.totalDuration ?: 0
/**
- * Creates and remembers a [Painter] to render this [AnimatedImageVector]. It renders the image
- * either at the start or the end of all the animations depending on the [atEnd]. Changes to
- * [atEnd] are animated.
- *
- * @param atEnd Whether the animated vector should be rendered at the end of all its animations.
- *
- * @sample androidx.compose.animation.graphics.samples.AnimatedVectorSample
+ * Provide an empty companion object to hang platform-specific companion extensions onto.
*/
- @Composable
- fun painterFor(atEnd: Boolean): Painter {
- return painterFor(atEnd) { group, overrides ->
- RenderVectorGroup(group, overrides)
- }
- }
-
- @Composable
- internal fun painterFor(
- atEnd: Boolean,
- render: @Composable (VectorGroup, Map<String, VectorConfig>) -> Unit
- ): Painter {
- return rememberVectorPainter(
- defaultWidth = imageVector.defaultWidth,
- defaultHeight = imageVector.defaultHeight,
- viewportWidth = imageVector.viewportWidth,
- viewportHeight = imageVector.viewportHeight,
- name = imageVector.name,
- tintColor = imageVector.tintColor,
- tintBlendMode = imageVector.tintBlendMode,
- ) { _, _ ->
- val transition = updateTransition(atEnd, label = imageVector.name)
- render(
- imageVector.root,
- targets.associate { target ->
- target.name to target.animator.createVectorConfig(transition, totalDuration)
- }
- )
- }
- }
+ companion object {} // ktlint-disable no-empty-class-body
}
/**
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVectorGraphicsDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVectorGraphicsDemo.kt
index 854a5d7..46dfdfc 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVectorGraphicsDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVectorGraphicsDemo.kt
@@ -25,6 +25,8 @@
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@@ -56,10 +58,10 @@
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally
) {
- val image = animatedVectorResource(R.drawable.ic_hourglass_animated)
+ val image = AnimatedImageVector.animatedVectorResource(R.drawable.ic_hourglass_animated)
var atEnd by remember { mutableStateOf(false) }
Image(
- painter = image.painterFor(atEnd),
+ painter = rememberAnimatedVectorPainter(image, atEnd),
contentDescription = "AnimatedImageVector",
modifier = Modifier.size(200.dp).clickable {
atEnd = !atEnd
diff --git a/compose/benchmark-utils/OWNERS b/compose/benchmark-utils/OWNERS
index 305021a..b2c0d56 100644
--- a/compose/benchmark-utils/OWNERS
+++ b/compose/benchmark-utils/OWNERS
@@ -1,2 +1 @@
-pavlis@google.com
jellefresen@google.com
diff --git a/compose/foundation/foundation/api/1.1.0-beta04.txt b/compose/foundation/foundation/api/1.1.0-beta04.txt
index d5185f0..d890160 100644
--- a/compose/foundation/foundation/api/1.1.0-beta04.txt
+++ b/compose/foundation/foundation/api/1.1.0-beta04.txt
@@ -372,9 +372,6 @@
package androidx.compose.foundation.lazy {
- public final class IntervalListKt {
- }
-
public final class LazyDslKt {
method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
@@ -396,9 +393,6 @@
method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
}
- public final class LazyListHeadersKt {
- }
-
public interface LazyListItemInfo {
method public int getIndex();
method public Object getKey();
@@ -410,15 +404,6 @@
property public abstract int size;
}
- public final class LazyListItemPlacementAnimatorKt {
- }
-
- public final class LazyListItemsProviderImplKt {
- }
-
- public final class LazyListKt {
- }
-
public interface LazyListLayoutInfo {
method public int getTotalItemsCount();
method public int getViewportEndOffset();
@@ -430,17 +415,11 @@
property public abstract java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> visibleItemsInfo;
}
- public final class LazyListMeasureKt {
- }
-
@androidx.compose.foundation.lazy.LazyScopeMarker public interface LazyListScope {
method public void item(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
}
- public final class LazyListScrollingKt {
- }
-
@androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
ctor public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
@@ -472,9 +451,6 @@
@kotlin.DslMarker public @interface LazyScopeMarker {
}
- public final class LazySemanticsKt {
- }
-
public final class Lazy_androidKt {
}
@@ -499,6 +475,34 @@
}
+package androidx.compose.foundation.lazy.list {
+
+ public final class IntervalListKt {
+ }
+
+ public final class LazyListHeadersKt {
+ }
+
+ public final class LazyListItemPlacementAnimatorKt {
+ }
+
+ public final class LazyListItemsProviderImplKt {
+ }
+
+ public final class LazyListKt {
+ }
+
+ public final class LazyListMeasureKt {
+ }
+
+ public final class LazyListScrollingKt {
+ }
+
+ public final class LazySemanticsKt {
+ }
+
+}
+
package androidx.compose.foundation.relocation {
public final class BringIntoViewRequesterKt {
diff --git a/compose/foundation/foundation/api/current.ignore b/compose/foundation/foundation/api/current.ignore
index a3cabae..1de8dc2 100644
--- a/compose/foundation/foundation/api/current.ignore
+++ b/compose/foundation/foundation/api/current.ignore
@@ -1,11 +1,17 @@
// Baseline format: 1.0
-InvalidNullConversion: androidx.compose.foundation.ImageKt#Image(androidx.compose.ui.graphics.ImageBitmap, String, androidx.compose.ui.Modifier, androidx.compose.ui.Alignment, androidx.compose.ui.layout.ContentScale, float, androidx.compose.ui.graphics.ColorFilter) parameter #1:
- Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter contentDescription in androidx.compose.foundation.ImageKt.Image(androidx.compose.ui.graphics.ImageBitmap bitmap, String contentDescription, androidx.compose.ui.Modifier modifier, androidx.compose.ui.Alignment alignment, androidx.compose.ui.layout.ContentScale contentScale, float alpha, androidx.compose.ui.graphics.ColorFilter colorFilter)
-
-
-RemovedClass: androidx.compose.foundation.gestures.RelativeVelocityTrackerKt:
- Removed class androidx.compose.foundation.gestures.RelativeVelocityTrackerKt
-RemovedClass: androidx.compose.foundation.lazy.LazyListItemContentFactoryKt:
- Removed class androidx.compose.foundation.lazy.LazyListItemContentFactoryKt
-RemovedClass: androidx.compose.foundation.lazy.LazyListPrefetcher_androidKt:
- Removed class androidx.compose.foundation.lazy.LazyListPrefetcher_androidKt
+RemovedClass: androidx.compose.foundation.lazy.IntervalListKt:
+ Removed class androidx.compose.foundation.lazy.IntervalListKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListHeadersKt:
+ Removed class androidx.compose.foundation.lazy.LazyListHeadersKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListItemPlacementAnimatorKt:
+ Removed class androidx.compose.foundation.lazy.LazyListItemPlacementAnimatorKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListItemsProviderImplKt:
+ Removed class androidx.compose.foundation.lazy.LazyListItemsProviderImplKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListKt:
+ Removed class androidx.compose.foundation.lazy.LazyListKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListMeasureKt:
+ Removed class androidx.compose.foundation.lazy.LazyListMeasureKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListScrollingKt:
+ Removed class androidx.compose.foundation.lazy.LazyListScrollingKt
+RemovedClass: androidx.compose.foundation.lazy.LazySemanticsKt:
+ Removed class androidx.compose.foundation.lazy.LazySemanticsKt
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index d5185f0..d890160 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -372,9 +372,6 @@
package androidx.compose.foundation.lazy {
- public final class IntervalListKt {
- }
-
public final class LazyDslKt {
method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
@@ -396,9 +393,6 @@
method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
}
- public final class LazyListHeadersKt {
- }
-
public interface LazyListItemInfo {
method public int getIndex();
method public Object getKey();
@@ -410,15 +404,6 @@
property public abstract int size;
}
- public final class LazyListItemPlacementAnimatorKt {
- }
-
- public final class LazyListItemsProviderImplKt {
- }
-
- public final class LazyListKt {
- }
-
public interface LazyListLayoutInfo {
method public int getTotalItemsCount();
method public int getViewportEndOffset();
@@ -430,17 +415,11 @@
property public abstract java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> visibleItemsInfo;
}
- public final class LazyListMeasureKt {
- }
-
@androidx.compose.foundation.lazy.LazyScopeMarker public interface LazyListScope {
method public void item(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
}
- public final class LazyListScrollingKt {
- }
-
@androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
ctor public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
@@ -472,9 +451,6 @@
@kotlin.DslMarker public @interface LazyScopeMarker {
}
- public final class LazySemanticsKt {
- }
-
public final class Lazy_androidKt {
}
@@ -499,6 +475,34 @@
}
+package androidx.compose.foundation.lazy.list {
+
+ public final class IntervalListKt {
+ }
+
+ public final class LazyListHeadersKt {
+ }
+
+ public final class LazyListItemPlacementAnimatorKt {
+ }
+
+ public final class LazyListItemsProviderImplKt {
+ }
+
+ public final class LazyListKt {
+ }
+
+ public final class LazyListMeasureKt {
+ }
+
+ public final class LazyListScrollingKt {
+ }
+
+ public final class LazySemanticsKt {
+ }
+
+}
+
package androidx.compose.foundation.relocation {
public final class BringIntoViewRequesterKt {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_1.1.0-beta04.txt b/compose/foundation/foundation/api/public_plus_experimental_1.1.0-beta04.txt
index 1e015d0..b9cf081 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_1.1.0-beta04.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_1.1.0-beta04.txt
@@ -427,9 +427,6 @@
ctor public GridItemSpan();
}
- public final class IntervalListKt {
- }
-
public final class LazyDslKt {
method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
@@ -472,9 +469,6 @@
method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
}
- public final class LazyListHeadersKt {
- }
-
public interface LazyListItemInfo {
method public int getIndex();
method public Object getKey();
@@ -486,15 +480,6 @@
property public abstract int size;
}
- public final class LazyListItemPlacementAnimatorKt {
- }
-
- public final class LazyListItemsProviderImplKt {
- }
-
- public final class LazyListKt {
- }
-
public interface LazyListLayoutInfo {
method public int getTotalItemsCount();
method public int getViewportEndOffset();
@@ -506,18 +491,12 @@
property public abstract java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> visibleItemsInfo;
}
- public final class LazyListMeasureKt {
- }
-
@androidx.compose.foundation.lazy.LazyScopeMarker public interface LazyListScope {
method public void item(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
method @androidx.compose.foundation.ExperimentalFoundationApi public void stickyHeader(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
}
- public final class LazyListScrollingKt {
- }
-
@androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
ctor public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
@@ -549,9 +528,6 @@
@kotlin.DslMarker public @interface LazyScopeMarker {
}
- public final class LazySemanticsKt {
- }
-
public final class Lazy_androidKt {
}
@@ -576,6 +552,34 @@
}
+package androidx.compose.foundation.lazy.list {
+
+ public final class IntervalListKt {
+ }
+
+ public final class LazyListHeadersKt {
+ }
+
+ public final class LazyListItemPlacementAnimatorKt {
+ }
+
+ public final class LazyListItemsProviderImplKt {
+ }
+
+ public final class LazyListKt {
+ }
+
+ public final class LazyListMeasureKt {
+ }
+
+ public final class LazyListScrollingKt {
+ }
+
+ public final class LazySemanticsKt {
+ }
+
+}
+
package androidx.compose.foundation.relocation {
@androidx.compose.foundation.ExperimentalFoundationApi public sealed interface BringIntoViewRequester {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index 1e015d0..b9cf081 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -427,9 +427,6 @@
ctor public GridItemSpan();
}
- public final class IntervalListKt {
- }
-
public final class LazyDslKt {
method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
@@ -472,9 +469,6 @@
method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
}
- public final class LazyListHeadersKt {
- }
-
public interface LazyListItemInfo {
method public int getIndex();
method public Object getKey();
@@ -486,15 +480,6 @@
property public abstract int size;
}
- public final class LazyListItemPlacementAnimatorKt {
- }
-
- public final class LazyListItemsProviderImplKt {
- }
-
- public final class LazyListKt {
- }
-
public interface LazyListLayoutInfo {
method public int getTotalItemsCount();
method public int getViewportEndOffset();
@@ -506,18 +491,12 @@
property public abstract java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> visibleItemsInfo;
}
- public final class LazyListMeasureKt {
- }
-
@androidx.compose.foundation.lazy.LazyScopeMarker public interface LazyListScope {
method public void item(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
method @androidx.compose.foundation.ExperimentalFoundationApi public void stickyHeader(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
}
- public final class LazyListScrollingKt {
- }
-
@androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
ctor public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
@@ -549,9 +528,6 @@
@kotlin.DslMarker public @interface LazyScopeMarker {
}
- public final class LazySemanticsKt {
- }
-
public final class Lazy_androidKt {
}
@@ -576,6 +552,34 @@
}
+package androidx.compose.foundation.lazy.list {
+
+ public final class IntervalListKt {
+ }
+
+ public final class LazyListHeadersKt {
+ }
+
+ public final class LazyListItemPlacementAnimatorKt {
+ }
+
+ public final class LazyListItemsProviderImplKt {
+ }
+
+ public final class LazyListKt {
+ }
+
+ public final class LazyListMeasureKt {
+ }
+
+ public final class LazyListScrollingKt {
+ }
+
+ public final class LazySemanticsKt {
+ }
+
+}
+
package androidx.compose.foundation.relocation {
@androidx.compose.foundation.ExperimentalFoundationApi public sealed interface BringIntoViewRequester {
diff --git a/compose/foundation/foundation/api/restricted_1.1.0-beta04.txt b/compose/foundation/foundation/api/restricted_1.1.0-beta04.txt
index d5185f0..d890160 100644
--- a/compose/foundation/foundation/api/restricted_1.1.0-beta04.txt
+++ b/compose/foundation/foundation/api/restricted_1.1.0-beta04.txt
@@ -372,9 +372,6 @@
package androidx.compose.foundation.lazy {
- public final class IntervalListKt {
- }
-
public final class LazyDslKt {
method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
@@ -396,9 +393,6 @@
method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
}
- public final class LazyListHeadersKt {
- }
-
public interface LazyListItemInfo {
method public int getIndex();
method public Object getKey();
@@ -410,15 +404,6 @@
property public abstract int size;
}
- public final class LazyListItemPlacementAnimatorKt {
- }
-
- public final class LazyListItemsProviderImplKt {
- }
-
- public final class LazyListKt {
- }
-
public interface LazyListLayoutInfo {
method public int getTotalItemsCount();
method public int getViewportEndOffset();
@@ -430,17 +415,11 @@
property public abstract java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> visibleItemsInfo;
}
- public final class LazyListMeasureKt {
- }
-
@androidx.compose.foundation.lazy.LazyScopeMarker public interface LazyListScope {
method public void item(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
}
- public final class LazyListScrollingKt {
- }
-
@androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
ctor public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
@@ -472,9 +451,6 @@
@kotlin.DslMarker public @interface LazyScopeMarker {
}
- public final class LazySemanticsKt {
- }
-
public final class Lazy_androidKt {
}
@@ -499,6 +475,34 @@
}
+package androidx.compose.foundation.lazy.list {
+
+ public final class IntervalListKt {
+ }
+
+ public final class LazyListHeadersKt {
+ }
+
+ public final class LazyListItemPlacementAnimatorKt {
+ }
+
+ public final class LazyListItemsProviderImplKt {
+ }
+
+ public final class LazyListKt {
+ }
+
+ public final class LazyListMeasureKt {
+ }
+
+ public final class LazyListScrollingKt {
+ }
+
+ public final class LazySemanticsKt {
+ }
+
+}
+
package androidx.compose.foundation.relocation {
public final class BringIntoViewRequesterKt {
diff --git a/compose/foundation/foundation/api/restricted_current.ignore b/compose/foundation/foundation/api/restricted_current.ignore
index a3cabae..1de8dc2 100644
--- a/compose/foundation/foundation/api/restricted_current.ignore
+++ b/compose/foundation/foundation/api/restricted_current.ignore
@@ -1,11 +1,17 @@
// Baseline format: 1.0
-InvalidNullConversion: androidx.compose.foundation.ImageKt#Image(androidx.compose.ui.graphics.ImageBitmap, String, androidx.compose.ui.Modifier, androidx.compose.ui.Alignment, androidx.compose.ui.layout.ContentScale, float, androidx.compose.ui.graphics.ColorFilter) parameter #1:
- Attempted to change parameter from @Nullable to @NonNull: incompatible change for parameter contentDescription in androidx.compose.foundation.ImageKt.Image(androidx.compose.ui.graphics.ImageBitmap bitmap, String contentDescription, androidx.compose.ui.Modifier modifier, androidx.compose.ui.Alignment alignment, androidx.compose.ui.layout.ContentScale contentScale, float alpha, androidx.compose.ui.graphics.ColorFilter colorFilter)
-
-
-RemovedClass: androidx.compose.foundation.gestures.RelativeVelocityTrackerKt:
- Removed class androidx.compose.foundation.gestures.RelativeVelocityTrackerKt
-RemovedClass: androidx.compose.foundation.lazy.LazyListItemContentFactoryKt:
- Removed class androidx.compose.foundation.lazy.LazyListItemContentFactoryKt
-RemovedClass: androidx.compose.foundation.lazy.LazyListPrefetcher_androidKt:
- Removed class androidx.compose.foundation.lazy.LazyListPrefetcher_androidKt
+RemovedClass: androidx.compose.foundation.lazy.IntervalListKt:
+ Removed class androidx.compose.foundation.lazy.IntervalListKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListHeadersKt:
+ Removed class androidx.compose.foundation.lazy.LazyListHeadersKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListItemPlacementAnimatorKt:
+ Removed class androidx.compose.foundation.lazy.LazyListItemPlacementAnimatorKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListItemsProviderImplKt:
+ Removed class androidx.compose.foundation.lazy.LazyListItemsProviderImplKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListKt:
+ Removed class androidx.compose.foundation.lazy.LazyListKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListMeasureKt:
+ Removed class androidx.compose.foundation.lazy.LazyListMeasureKt
+RemovedClass: androidx.compose.foundation.lazy.LazyListScrollingKt:
+ Removed class androidx.compose.foundation.lazy.LazyListScrollingKt
+RemovedClass: androidx.compose.foundation.lazy.LazySemanticsKt:
+ Removed class androidx.compose.foundation.lazy.LazySemanticsKt
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index d5185f0..d890160 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -372,9 +372,6 @@
package androidx.compose.foundation.lazy {
- public final class IntervalListKt {
- }
-
public final class LazyDslKt {
method @androidx.compose.runtime.Composable public static void LazyColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void LazyRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.LazyListState state, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional boolean reverseLayout, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Vertical verticalAlignment, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyListScope,kotlin.Unit> content);
@@ -396,9 +393,6 @@
method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
}
- public final class LazyListHeadersKt {
- }
-
public interface LazyListItemInfo {
method public int getIndex();
method public Object getKey();
@@ -410,15 +404,6 @@
property public abstract int size;
}
- public final class LazyListItemPlacementAnimatorKt {
- }
-
- public final class LazyListItemsProviderImplKt {
- }
-
- public final class LazyListKt {
- }
-
public interface LazyListLayoutInfo {
method public int getTotalItemsCount();
method public int getViewportEndOffset();
@@ -430,17 +415,11 @@
property public abstract java.util.List<androidx.compose.foundation.lazy.LazyListItemInfo> visibleItemsInfo;
}
- public final class LazyListMeasureKt {
- }
-
@androidx.compose.foundation.lazy.LazyScopeMarker public interface LazyListScope {
method public void item(optional Object? key, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.lazy.LazyItemScope,kotlin.Unit> content);
method public void items(int count, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,?>? key, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.LazyItemScope,? super java.lang.Integer,kotlin.Unit> itemContent);
}
- public final class LazyListScrollingKt {
- }
-
@androidx.compose.runtime.Stable public final class LazyListState implements androidx.compose.foundation.gestures.ScrollableState {
ctor public LazyListState(optional int firstVisibleItemIndex, optional int firstVisibleItemScrollOffset);
method public suspend Object? animateScrollToItem(int index, optional int scrollOffset, optional kotlin.coroutines.Continuation<? super kotlin.Unit> p);
@@ -472,9 +451,6 @@
@kotlin.DslMarker public @interface LazyScopeMarker {
}
- public final class LazySemanticsKt {
- }
-
public final class Lazy_androidKt {
}
@@ -499,6 +475,34 @@
}
+package androidx.compose.foundation.lazy.list {
+
+ public final class IntervalListKt {
+ }
+
+ public final class LazyListHeadersKt {
+ }
+
+ public final class LazyListItemPlacementAnimatorKt {
+ }
+
+ public final class LazyListItemsProviderImplKt {
+ }
+
+ public final class LazyListKt {
+ }
+
+ public final class LazyListMeasureKt {
+ }
+
+ public final class LazyListScrollingKt {
+ }
+
+ public final class LazySemanticsKt {
+ }
+
+}
+
package androidx.compose.foundation.relocation {
public final class BringIntoViewRequesterKt {
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/lazy/LazyListScrollingBenchmark.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/lazy/LazyListScrollingBenchmark.kt
index 036d159..20133f1 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/lazy/LazyListScrollingBenchmark.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/lazy/LazyListScrollingBenchmark.kt
@@ -20,6 +20,8 @@
import android.view.MotionEvent
import android.view.View
import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollScope
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
@@ -165,7 +167,11 @@
"LazyColumn",
isVertical = true
) { state ->
- LazyColumn(state = state, modifier = Modifier.requiredHeight(400.dp).fillMaxWidth()) {
+ LazyColumn(
+ state = state,
+ modifier = Modifier.requiredHeight(400.dp).fillMaxWidth(),
+ flingBehavior = NoFlingBehavior
+ ) {
item {
FirstLargeItem()
}
@@ -179,7 +185,11 @@
"LazyRow",
isVertical = false
) { state ->
- LazyRow(state = state, modifier = Modifier.requiredWidth(400.dp).fillMaxHeight()) {
+ LazyRow(
+ state = state,
+ modifier = Modifier.requiredWidth(400.dp).fillMaxHeight(),
+ flingBehavior = NoFlingBehavior
+ ) {
item {
FirstLargeItem()
}
@@ -189,6 +199,12 @@
}
}
+private object NoFlingBehavior : FlingBehavior {
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ return 0f
+ }
+}
+
// TODO(b/169852102 use existing public constructs instead)
private fun ComposeBenchmarkRule.toggleStateBenchmark(
caseFactory: () -> ListRemeasureTestCase
@@ -205,6 +221,7 @@
runWithTimingDisabled {
assertNoPendingChanges()
getTestCase().afterToggle()
+ assertNoPendingChanges()
}
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollAccessibilityTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollAccessibilityTest.kt
index 57511e1..4804278 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollAccessibilityTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ScrollAccessibilityTest.kt
@@ -93,7 +93,7 @@
}.let { composeView ->
ViewCompat
.getAccessibilityDelegate(composeView)!!
- .getAccessibilityNodeProvider(composeView)
+ .getAccessibilityNodeProvider(composeView)!!
.provider as AccessibilityNodeProvider
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TransformableTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TransformableTest.kt
index 073fad1..3fb977b 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TransformableTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/TransformableTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.foundation
+import androidx.compose.animation.core.tween
import androidx.compose.foundation.gestures.TransformableState
import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.gestures.animatePanBy
@@ -29,7 +30,9 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.InspectableValue
@@ -37,6 +40,7 @@
import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performTouchInput
@@ -47,6 +51,9 @@
import androidx.test.filters.MediumTest
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@@ -66,6 +73,8 @@
@get:Rule
val rule = createComposeRule()
+ private lateinit var scope: CoroutineScope
+
@Before
fun before() {
isDebugInspectorInfoEnabled = true
@@ -76,6 +85,14 @@
isDebugInspectorInfoEnabled = false
}
+ private fun ComposeContentTestRule.setContentAndGetScope(content: @Composable () -> Unit) {
+ setContent {
+ val actualScope = rememberCoroutineScope()
+ SideEffect { scope = actualScope }
+ content()
+ }
+ }
+
@Test
fun transformable_zoomIn() {
var cumulativeScale = 1.0f
@@ -564,6 +581,36 @@
}
@Test
+ fun transformable_animateCancelledUpdatesIsTransformInProgress() {
+ rule.mainClock.autoAdvance = false
+ val state = TransformableState { _, _, _ -> }
+ setTransformableContent {
+ Modifier.transformable(state)
+ }
+
+ lateinit var animateJob: Job
+
+ rule.runOnIdle {
+ assertThat(state.isTransformInProgress).isFalse()
+ animateJob = scope.launch {
+ state.animateZoomBy(4f, tween(1000))
+ }
+ }
+
+ rule.mainClock.advanceTimeBy(500)
+
+ rule.runOnIdle {
+ assertThat(state.isTransformInProgress).isTrue()
+ }
+
+ animateJob.cancel()
+
+ rule.runOnIdle {
+ assertThat(state.isTransformInProgress).isFalse()
+ }
+ }
+
+ @Test
fun testInspectorValue() {
rule.setContent {
val state = rememberTransformableState { _, _, _ -> }
@@ -579,7 +626,7 @@
}
private fun setTransformableContent(getModifier: @Composable () -> Modifier) {
- rule.setContent {
+ rule.setContentAndGetScope {
Box(Modifier.size(600.dp).testTag(TEST_TAG).then(getModifier()))
}
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyGridTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyGridTest.kt
index 439759c0d..92e395d 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyGridTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyGridTest.kt
@@ -29,6 +29,8 @@
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.list.scrollBy
+import androidx.compose.foundation.lazy.list.setContentWithTestViewConfiguration
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyArrangementsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyArrangementsTest.kt
similarity index 98%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyArrangementsTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyArrangementsTest.kt
index c22018e..48bb5d9 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyArrangementsTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyArrangementsTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Arrangement
@@ -22,6 +22,10 @@
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyColumnTest.kt
similarity index 98%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyColumnTest.kt
index 4673083..563d2bd 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyColumnTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyColumnTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import android.os.Build
import androidx.compose.foundation.AutoTestFrameClock
@@ -28,6 +28,10 @@
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyCustomKeysTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyCustomKeysTest.kt
similarity index 96%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyCustomKeysTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyCustomKeysTest.kt
index cfec1ba..fcb0408 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyCustomKeysTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyCustomKeysTest.kt
@@ -14,12 +14,18 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyItemStateRestoration.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyItemStateRestoration.kt
similarity index 96%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyItemStateRestoration.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyItemStateRestoration.kt
index 8604782..4178d65 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyItemStateRestoration.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyItemStateRestoration.kt
@@ -14,10 +14,15 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListAnimateItemPlacementTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListAnimateItemPlacementTest.kt
similarity index 98%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListAnimateItemPlacementTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListAnimateItemPlacementTest.kt
index dd77069..8ea615e 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListAnimateItemPlacementTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListAnimateItemPlacementTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.LinearEasing
@@ -29,6 +29,13 @@
import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.requiredWidthIn
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyItemScope
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListHeadersTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListHeadersTest.kt
similarity index 97%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListHeadersTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListHeadersTest.kt
index b45ea16..13b1f6d 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListHeadersTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListHeadersTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.PaddingValues
@@ -22,6 +22,11 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfoTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListLayoutInfoTest.kt
similarity index 96%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfoTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListLayoutInfoTest.kt
index d081014..143a455 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListLayoutInfoTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListLayoutInfoTest.kt
@@ -14,12 +14,17 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListLayoutInfo
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListPrefetcherTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetcherTest.kt
similarity index 97%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListPrefetcherTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetcherTest.kt
index 6c8a3e4..763ee14 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListPrefetcherTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListPrefetcherTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.AutoTestFrameClock
import androidx.compose.foundation.ExperimentalFoundationApi
@@ -22,6 +22,9 @@
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Remeasurement
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListSlotsReuseTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
similarity index 97%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListSlotsReuseTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
index 8f626d0..f9d6f61 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListSlotsReuseTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt
similarity index 98%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt
index e48ffbb..2b161b6 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import android.os.Build
import androidx.compose.animation.core.snap
@@ -39,6 +39,14 @@
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyItemScope
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsContentPaddingTest.kt
similarity index 98%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsContentPaddingTest.kt
index 9be61a2..5d0eea5 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsContentPaddingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsContentPaddingTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.animation.core.snap
import androidx.compose.foundation.AutoTestFrameClock
@@ -25,6 +25,11 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertHeightIsEqualTo
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsIndexedTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsIndexedTest.kt
similarity index 95%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsIndexedTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsIndexedTest.kt
index 78ad549..f104ca7 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsIndexedTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsIndexedTest.kt
@@ -14,13 +14,16 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.text.BasicText
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsReverseLayoutTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsReverseLayoutTest.kt
similarity index 97%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsReverseLayoutTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsReverseLayoutTest.kt
index 8ad1b08..3a47a97 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyListsReverseLayoutTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListsReverseLayoutTest.kt
@@ -14,10 +14,15 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyNestedScrollingTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyNestedScrollingTest.kt
similarity index 98%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyNestedScrollingTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyNestedScrollingTest.kt
index 3670335..78a5fdc 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyNestedScrollingTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyNestedScrollingTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.gestures.ScrollableState
import androidx.compose.foundation.gestures.scrollable
@@ -24,6 +24,9 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyRowTest.kt
similarity index 94%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyRowTest.kt
index d6b11c6..69f89a5 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyRowTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyRowTest.kt
@@ -14,13 +14,17 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollAccessibilityTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollAccessibilityTest.kt
similarity index 96%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollAccessibilityTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollAccessibilityTest.kt
index 7f03801..4cdc602 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollAccessibilityTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollAccessibilityTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import android.view.View
import android.view.accessibility.AccessibilityNodeProvider
@@ -23,6 +23,10 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
@@ -90,7 +94,7 @@
}.let { composeView ->
ViewCompat
.getAccessibilityDelegate(composeView)!!
- .getAccessibilityNodeProvider(composeView)
+ .getAccessibilityNodeProvider(composeView)!!
.provider as AccessibilityNodeProvider
}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
similarity index 93%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
index 689da8c..78d7333 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazyScrollTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazyScrollTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.AutoTestFrameClock
import androidx.compose.foundation.ExperimentalFoundationApi
@@ -25,6 +25,11 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.dp
import androidx.test.filters.MediumTest
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazySemanticsTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazySemanticsTest.kt
similarity index 96%
rename from compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazySemanticsTest.kt
rename to compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazySemanticsTest.kt
index a0b1bf3..e7fc7c84 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/LazySemanticsTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/list/LazySemanticsTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
@@ -22,6 +22,9 @@
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.requiredWidth
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/TempListUtils.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/TempListUtils.kt
index 0d55ea2..34d6bae 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/TempListUtils.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/TempListUtils.kt
@@ -25,6 +25,10 @@
/**
* Returns a list containing only elements matching the given [predicate].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastFilter(predicate: (T) -> Boolean): List<T> {
@@ -42,6 +46,10 @@
*
* Returns the specified [initial] value if the collection is empty.
*
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
+ *
* @param [operation] function that takes current accumulator value and an element, and calculates the next accumulator value.
*/
@OptIn(ExperimentalContracts::class)
@@ -57,6 +65,10 @@
/**
* Returns a list containing the results of applying the given [transform] function
* to each element in the original collection.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R> List<T>.fastMapIndexedNotNull(
@@ -73,6 +85,10 @@
/**
* Returns the largest value among all values produced by selector function applied to each element
* in the collection or null if there are no elements.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R : Comparable<R>> List<T>.fastMaxOfOrNull(selector: (T) -> R): R? {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TransformableState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TransformableState.kt
index 5298664..1e5d2b7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TransformableState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/TransformableState.kt
@@ -245,8 +245,11 @@
): Unit = coroutineScope {
transformMutex.mutateWith(transformScope, transformPriority) {
isTransformingState.value = true
- block()
- isTransformingState.value = false
+ try {
+ block()
+ } finally {
+ isTransformingState.value = false
+ }
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
index b1af308..b78cfa9 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
@@ -21,6 +21,7 @@
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.lazy.list.LazyList
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -259,3 +260,11 @@
content = content
)
}
+
+/**
+ * This should create an object meeting following requirements:
+ * 1) objects created for the same index are equals and never equals for different indexes
+ * 2) this class is saveable via a default SaveableStateRegistry on the platform
+ * 3) this objects can't be equals to any object which could be provided by a user as a custom key
+ */
+internal expect fun getDefaultLazyKeyFor(index: Int): Any
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt
index 5ba59cd..505a344 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyGrid.kt
@@ -23,6 +23,9 @@
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.lazy.list.IntervalHolder
+import androidx.compose.foundation.lazy.list.MutableIntervalList
+import androidx.compose.foundation.lazy.list.intervalForIndex
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt
index f26ebe8..fc68dfe 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyItemScope.kt
@@ -21,18 +21,9 @@
import androidx.compose.animation.core.VisibilityThreshold
import androidx.compose.animation.core.spring
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.ParentDataModifier
-import androidx.compose.ui.platform.InspectorInfo
-import androidx.compose.ui.platform.InspectorValueInfo
-import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
/**
@@ -106,46 +97,3 @@
)
): Modifier
}
-
-internal data class LazyItemScopeImpl(
- val density: Density,
- val constraints: Constraints
-) : LazyItemScope {
- private val maxWidth: Dp = with(density) { constraints.maxWidth.toDp() }
- private val maxHeight: Dp = with(density) { constraints.maxHeight.toDp() }
-
- override fun Modifier.fillParentMaxSize(fraction: Float) = size(
- maxWidth * fraction,
- maxHeight * fraction
- )
-
- override fun Modifier.fillParentMaxWidth(fraction: Float) =
- width(maxWidth * fraction)
-
- override fun Modifier.fillParentMaxHeight(fraction: Float) =
- height(maxHeight * fraction)
-
- @ExperimentalFoundationApi
- override fun Modifier.animateItemPlacement(animationSpec: FiniteAnimationSpec<IntOffset>) =
- this.then(AnimateItemPlacementModifier(animationSpec, debugInspectorInfo {
- name = "animateItemPlacement"
- value = animationSpec
- }))
-}
-
-private class AnimateItemPlacementModifier(
- val animationSpec: FiniteAnimationSpec<IntOffset>,
- inspectorInfo: InspectorInfo.() -> Unit,
-) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
- override fun Density.modifyParentData(parentData: Any?): Any = animationSpec
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is AnimateItemPlacementModifier) return false
- return animationSpec != other.animationSpec
- }
-
- override fun hashCode(): Int {
- return animationSpec.hashCode()
- }
-}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
index 04c44c0..92e6f17 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
@@ -24,6 +24,12 @@
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchPolicy
import androidx.compose.foundation.lazy.layout.LazyLayoutState
+import androidx.compose.foundation.lazy.list.DataIndex
+import androidx.compose.foundation.lazy.list.LazyListItemPlacementAnimator
+import androidx.compose.foundation.lazy.list.LazyListItemsProvider
+import androidx.compose.foundation.lazy.list.LazyListMeasureResult
+import androidx.compose.foundation.lazy.list.LazyListScrollPosition
+import androidx.compose.foundation.lazy.list.doSmoothScrollToItem
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/DataIndex.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/DataIndex.kt
similarity index 96%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/DataIndex.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/DataIndex.kt
index aa3c299..49dc44f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/DataIndex.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/DataIndex.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
/**
* Represents an index in the list of items of lazy layout.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/IntervalList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/IntervalList.kt
similarity index 97%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/IntervalList.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/IntervalList.kt
index 8e2f14d..2fd5787 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/IntervalList.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/IntervalList.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
internal class IntervalHolder<T>(
val startIndex: Int,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyItemScopeImpl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyItemScopeImpl.kt
new file mode 100644
index 0000000..f7262c5
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyItemScopeImpl.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.compose.foundation.lazy.list
+
+import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyItemScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ParentDataModifier
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntOffset
+
+internal data class LazyItemScopeImpl(
+ val density: Density,
+ val constraints: Constraints
+) : LazyItemScope {
+ private val maxWidth: Dp = with(density) { constraints.maxWidth.toDp() }
+ private val maxHeight: Dp = with(density) { constraints.maxHeight.toDp() }
+
+ override fun Modifier.fillParentMaxSize(fraction: Float) = size(
+ maxWidth * fraction,
+ maxHeight * fraction
+ )
+
+ override fun Modifier.fillParentMaxWidth(fraction: Float) =
+ width(maxWidth * fraction)
+
+ override fun Modifier.fillParentMaxHeight(fraction: Float) =
+ height(maxHeight * fraction)
+
+ @ExperimentalFoundationApi
+ override fun Modifier.animateItemPlacement(animationSpec: FiniteAnimationSpec<IntOffset>) =
+ this.then(AnimateItemPlacementModifier(animationSpec, debugInspectorInfo {
+ name = "animateItemPlacement"
+ value = animationSpec
+ }))
+}
+
+private class AnimateItemPlacementModifier(
+ val animationSpec: FiniteAnimationSpec<IntOffset>,
+ inspectorInfo: InspectorInfo.() -> Unit,
+) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
+ override fun Density.modifyParentData(parentData: Any?): Any = animationSpec
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is AnimateItemPlacementModifier) return false
+ return animationSpec != other.animationSpec
+ }
+
+ override fun hashCode(): Int {
+ return animationSpec.hashCode()
+ }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyList.kt
similarity index 98%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyList.kt
index 635c4af..46f14c3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyList.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.assertNotNestingScrollableContainers
import androidx.compose.foundation.clipScrollableContainer
@@ -28,6 +28,8 @@
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.layout.LazyLayout
import androidx.compose.foundation.lazy.layout.LazyMeasurePolicy
import androidx.compose.foundation.lazy.layout.rememberLazyLayoutPrefetchPolicy
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListHeaders.kt
similarity index 98%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListHeaders.kt
index 9816176..7355e96 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListHeaders.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.ui.util.fastForEachIndexed
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListItemPlacementAnimator.kt
similarity index 99%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListItemPlacementAnimator.kt
index 11f4c1a..7243fbe 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListItemPlacementAnimator.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.Spring
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListItemsProvider.kt
similarity index 94%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListItemsProvider.kt
index d721bff..1d03253 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListItemsProvider.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.lazy.layout.LazyLayoutItemsProvider
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProviderImpl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListItemsProviderImpl.kt
similarity index 91%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProviderImpl.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListItemsProviderImpl.kt
index e704e24..6eda8b9 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProviderImpl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListItemsProviderImpl.kt
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
+import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.getDefaultLazyKeyFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
@@ -83,19 +86,11 @@
}
/**
- * This should create an object meeting following requirements:
- * 1) objects created for the same index are equals and never equals for different indexes
- * 2) this class is saveable via a default SaveableStateRegistry on the platform
- * 3) this objects can't be equals to any object which could be provided by a user as a custom key
- */
-internal expect fun getDefaultLazyKeyFor(index: Int): Any
-
-/**
* Traverses the interval [list] in order to create a mapping from the key to the index for all
* the indexes in the passed [range].
* The returned map will not contain the values for intervals with no key mapping provided.
*/
-private fun generateKeyToIndexMap(
+internal fun generateKeyToIndexMap(
range: IntRange,
list: IntervalList<LazyListIntervalContent>
): Map<Any, Int> {
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/list/LazyListMeasure.kt
similarity index 99%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListMeasure.kt
index 11e9300..ef95dd9 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/list/LazyListMeasure.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.ui.layout.MeasureResult
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasureResult.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListMeasureResult.kt
similarity index 93%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasureResult.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListMeasureResult.kt
index a1c3315..4f5d0003 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasureResult.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListMeasureResult.kt
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
+import androidx.compose.foundation.lazy.LazyListItemInfo
+import androidx.compose.foundation.lazy.LazyListLayoutInfo
import androidx.compose.foundation.lazy.layout.LazyLayoutItemInfo
import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureResult
import androidx.compose.ui.layout.MeasureResult
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScopeImpl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListScopeImpl.kt
similarity index 93%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScopeImpl.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListScopeImpl.kt
index 4e9771e..8feb771 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScopeImpl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListScopeImpl.kt
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.lazy.LazyItemScope
+import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.Composable
internal class LazyListScopeImpl : LazyListScope {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListScrollPosition.kt
similarity index 98%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListScrollPosition.kt
index 858a091..b51e2ee 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListScrollPosition.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.runtime.mutableStateOf
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrolling.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListScrolling.kt
similarity index 98%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrolling.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListScrolling.kt
index 1ddc32e..552a51e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrolling.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyListScrolling.kt
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.AnimationState
import androidx.compose.animation.core.animateTo
import androidx.compose.animation.core.spring
+import androidx.compose.foundation.lazy.LazyListItemInfo
+import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastFirstOrNull
import kotlin.coroutines.cancellation.CancellationException
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItem.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyMeasuredItem.kt
similarity index 98%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItem.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyMeasuredItem.kt
index 917f1cd..e35218f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItem.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyMeasuredItem.kt
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.animation.core.FiniteAnimationSpec
+import androidx.compose.foundation.lazy.LazyListItemInfo
import androidx.compose.foundation.lazy.layout.LazyLayoutPlaceable
import androidx.compose.ui.Alignment
import androidx.compose.ui.layout.Placeable
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyMeasuredItemProvider.kt
similarity index 97%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItemProvider.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyMeasuredItemProvider.kt
index eda6892..030b5fb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyMeasuredItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazyMeasuredItemProvider.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.lazy.layout.LazyLayoutPlaceable
import androidx.compose.foundation.lazy.layout.LazyLayoutPlaceablesProvider
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazySemantics.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazySemantics.kt
similarity index 97%
rename from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazySemantics.kt
rename to compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazySemantics.kt
index 8f949991..7ecbf46 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazySemantics.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/list/LazySemantics.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.compose.foundation.lazy.list
import androidx.compose.foundation.gestures.ScrollableState
import androidx.compose.foundation.gestures.animateScrollBy
+import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.CollectionInfo
diff --git a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
index 2a9e88f..3ecc5c7 100644
--- a/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
+++ b/compose/foundation/foundation/src/desktopTest/kotlin/androidx/compose/foundation/ScrollbarTest.kt
@@ -33,13 +33,11 @@
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.input.mouse.MouseScrollOrientation
-import androidx.compose.ui.input.mouse.MouseScrollUnit
-import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.InternalTestApi
@@ -432,10 +430,10 @@
@OptIn(InternalTestApi::class, ExperimentalComposeUiApi::class)
private fun ComposeTestRule.performMouseScroll(x: Int, y: Int, delta: Float) {
- (this as DesktopComposeTestRule).scene.sendPointerScrollEvent(
+ (this as DesktopComposeTestRule).scene.sendPointerEvent(
+ PointerEventType.Move,
Offset(x.toFloat(), y.toFloat()),
- MouseScrollUnit.Line(delta),
- MouseScrollOrientation.Vertical
+ scrollDelta = Offset(x = 0f, y = delta)
)
}
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt
index 844d010..c225449 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/gestures/TapGestureDetectorTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.foundation.gestures
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.input.pointer.consumeDownChange
import androidx.compose.ui.input.pointer.consumePositionChange
import kotlinx.coroutines.delay
@@ -108,10 +109,12 @@
/**
* Clicking in the region should result in the callback being invoked.
*/
+ @OptIn(ExperimentalComposeUiApi::class)
@Test
fun normalTap() = util.executeInComposition {
val down = down(5f, 5f)
assertTrue(down.consumed.downChange)
+ assertTrue(down.isConsumed)
assertTrue(pressed)
assertFalse(tapped)
@@ -119,6 +122,7 @@
val up = down.up(50)
assertTrue(up.consumed.downChange)
+ assertTrue(up.isConsumed)
assertTrue(tapped)
assertTrue(released)
@@ -224,6 +228,7 @@
* Pressing in the region, sliding out and then lifting should result in
* the callback not being invoked
*/
+ @OptIn(ExperimentalComposeUiApi::class)
@Test
fun tapMiss() = util.executeInComposition {
val up = down(5f, 5f)
@@ -235,6 +240,7 @@
assertFalse(released)
assertFalse(tapped)
assertFalse(up.consumed.downChange)
+ assertFalse(up.isConsumed)
}
/**
@@ -467,9 +473,46 @@
assertTrue(canceled)
}
+ @OptIn(ExperimentalComposeUiApi::class)
+ @Test
+ fun consumedChange_MotionTap() = util.executeInComposition {
+ down(5f, 5f)
+ .moveTo(6f, 2f) {
+ consume()
+ }
+ .up(50)
+
+ assertFalse(tapped)
+ assertTrue(pressed)
+ assertFalse(released)
+ assertTrue(canceled)
+ }
+
+ /**
+ * Clicking in the region with the up already consumed should result in the callback not
+ * being invoked.
+ */
+ @OptIn(ExperimentalComposeUiApi::class)
+ @Test
+ fun consumedChange_upTap() = util.executeInComposition {
+ val down = down(5f, 5f)
+
+ assertFalse(tapped)
+ assertTrue(pressed)
+
+ down.up {
+ consume()
+ }
+
+ assertFalse(tapped)
+ assertFalse(released)
+ assertTrue(canceled)
+ }
+
/**
* Ensure that two-finger taps work.
*/
+ @OptIn(ExperimentalComposeUiApi::class)
@Test
fun twoFingerTap() = util.executeInComposition {
val down = down(1f, 1f)
@@ -480,16 +523,19 @@
val down2 = down(9f, 5f)
assertFalse(down2.consumed.downChange)
+ assertFalse(down2.isConsumed)
assertFalse(pressed)
val up = down.up()
assertFalse(up.consumed.downChange)
+ assertFalse(up.isConsumed)
assertFalse(tapped)
assertFalse(released)
val up2 = down2.up()
assertTrue(up2.consumed.downChange)
+ assertTrue(up2.isConsumed)
assertTrue(tapped)
assertTrue(released)
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/lazy/MutableIntervalListTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/lazy/MutableIntervalListTest.kt
index d909f6a..e4aff73 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/lazy/MutableIntervalListTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/lazy/MutableIntervalListTest.kt
@@ -16,6 +16,8 @@
package androidx.compose.foundation.lazy
+import androidx.compose.foundation.lazy.list.MutableIntervalList
+import androidx.compose.foundation.lazy.list.intervalForIndex
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/compose/integration-tests/demos/OWNERS b/compose/integration-tests/demos/OWNERS
index 49be039..cb0413de 100644
--- a/compose/integration-tests/demos/OWNERS
+++ b/compose/integration-tests/demos/OWNERS
@@ -1,4 +1,3 @@
-pavlis@google.com
adamp@google.com
mount@google.com
popam@google.com
diff --git a/compose/integration-tests/demos/build.gradle b/compose/integration-tests/demos/build.gradle
index 00e53eb..cec8a8f 100644
--- a/compose/integration-tests/demos/build.gradle
+++ b/compose/integration-tests/demos/build.gradle
@@ -22,6 +22,7 @@
implementation(project(":compose:foundation:foundation-layout"))
implementation(project(":compose:integration-tests:demos:common"))
implementation(project(":compose:material:material"))
+ implementation(project(":compose:material3:material3"))
implementation(project(":compose:runtime:runtime"))
implementation(project(":compose:ui:ui"))
diff --git a/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt b/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt
index 6d4f247..0684a01 100644
--- a/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt
+++ b/compose/integration-tests/demos/src/androidTest/java/androidx/compose/integration/demos/test/DemoTest.kt
@@ -41,6 +41,7 @@
import androidx.test.filters.LargeTest
import androidx.test.filters.MediumTest
import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -107,6 +108,8 @@
navigateThroughAllDemos(SplitDemoCategories[2])
}
+ // Broken: b/206811195
+ @Ignore
@Test
fun navigateThroughAllDemos_4() {
navigateThroughAllDemos(SplitDemoCategories[3])
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt
index b438f13..0fa2230 100644
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoActivity.kt
@@ -17,22 +17,23 @@
package androidx.compose.integration.demos
import android.app.Activity
-import android.content.Context
import android.content.Intent
-import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import android.view.Window
import androidx.activity.OnBackPressedCallback
import androidx.activity.OnBackPressedDispatcher
+import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.integration.demos.common.ActivityDemo
import androidx.compose.integration.demos.common.Demo
import androidx.compose.integration.demos.common.DemoCategory
-import androidx.compose.material.Colors
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.darkColors
-import androidx.compose.material.lightColors
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -45,12 +46,12 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalView
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
-import androidx.preference.PreferenceManager
/**
* Main [Activity] containing all Compose related demos.
@@ -75,18 +76,19 @@
) {
Navigator(AllDemosCategory, onBackPressedDispatcher, activityStarter)
}
- val demoColors = remember {
- DemoColors().also {
- lifecycle.addObserver(
- LifecycleEventObserver { _, event ->
- if (event == Lifecycle.Event.ON_RESUME) {
- it.loadColorsFromSharedPreferences(this)
- }
- }
- )
+ val isDynamicThemeOn = remember { mutableStateOf(IsDynamicThemingAvailable) }
+ DisposableEffect(lifecycle) {
+ val obs = LifecycleEventObserver { _, event ->
+ if (event == Lifecycle.Event.ON_RESUME) {
+ isDynamicThemeOn.value = isDynamicThemeSettingOn(applicationContext)
+ }
+ }
+ lifecycle.addObserver(obs)
+ onDispose {
+ lifecycle.removeObserver(obs)
}
}
- DemoTheme(demoColors, window) {
+ DemoTheme(isDynamicThemeOn.value, window) {
val filteringMode = rememberSaveable(
saver = FilterMode.Saver(onBackPressedDispatcher)
) {
@@ -122,19 +124,26 @@
@Composable
private fun DemoTheme(
- demoColors: DemoColors,
+ isDynamicThemeOn: Boolean,
window: Window,
content: @Composable () -> Unit
) {
- MaterialTheme(demoColors.colors) {
- val statusBarColor = with(MaterialTheme.colors) {
- (if (isLight) primaryVariant else Color.Black).toArgb()
+ val isDarkMode = isSystemInDarkTheme()
+
+ @Suppress("NewApi")
+ val colorScheme =
+ if (isDynamicThemeOn) {
+ val context = LocalContext.current
+ if (isDarkMode) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ } else {
+ if (isDarkMode) darkColorScheme() else lightColorScheme()
}
- SideEffect {
- window.statusBarColor = statusBarColor
- }
- content()
+
+ SideEffect {
+ window.statusBarColor =
+ (if (isDarkMode) Color.Black else colorScheme.inversePrimary).toArgb()
}
+ MaterialTheme(colorScheme = colorScheme, content = content)
}
private class Navigator private constructor(
@@ -254,95 +263,3 @@
)
}
}
-
-/**
- * Returns a [DemoColors] from the values saved to [SharedPreferences]. If a given color is
- * not present in the [SharedPreferences], its default value as defined in [Colors]
- * will be returned.
- */
-fun DemoColors.loadColorsFromSharedPreferences(context: Context) {
- val sharedPreferences =
- PreferenceManager.getDefaultSharedPreferences(context)
-
- fun getColorsFromSharedPreferences(isLightTheme: Boolean): Colors {
- val function = if (isLightTheme) ::reflectLightColors else ::reflectDarkColors
- val parametersToSet = function.parameters.mapNotNull { parameter ->
- val savedValue = sharedPreferences.getString(parameter.name + isLightTheme, "")
- if (savedValue.isNullOrBlank()) {
- null
- } else {
- // TODO: should be a Color(savedValue.toLong(16)) when b/154329050 is fixed
- val parsedColor = savedValue.toLong(16)
- parameter to parsedColor
- }
- }.toMap()
- return function.callBy(parametersToSet)
- }
-
- light = getColorsFromSharedPreferences(true)
- dark = getColorsFromSharedPreferences(false)
-}
-
-/**
- * TODO: remove after b/154329050 is fixed
- * Inline classes don't play well with reflection, so we want boxed classes for our
- * call to [lightColors].
- */
-internal fun reflectLightColors(
- primary: Long = 0xFF6200EE,
- primaryVariant: Long = 0xFF3700B3,
- secondary: Long = 0xFF03DAC6,
- secondaryVariant: Long = 0xFF018786,
- background: Long = 0xFFFFFFFF,
- surface: Long = 0xFFFFFFFF,
- error: Long = 0xFFB00020,
- onPrimary: Long = 0xFFFFFFFF,
- onSecondary: Long = 0xFF000000,
- onBackground: Long = 0xFF000000,
- onSurface: Long = 0xFF000000,
- onError: Long = 0xFFFFFFFF
-) = lightColors(
- primary = Color(primary),
- primaryVariant = Color(primaryVariant),
- secondary = Color(secondary),
- secondaryVariant = Color(secondaryVariant),
- background = Color(background),
- surface = Color(surface),
- error = Color(error),
- onPrimary = Color(onPrimary),
- onSecondary = Color(onSecondary),
- onBackground = Color(onBackground),
- onSurface = Color(onSurface),
- onError = Color(onError)
-)
-
-/**
- * TODO: remove after b/154329050 is fixed
- * Inline classes don't play well with reflection, so we want boxed classes for our
- * call to [darkColors].
- */
-internal fun reflectDarkColors(
- primary: Long = 0xFFBB86FC,
- primaryVariant: Long = 0xFF3700B3,
- secondary: Long = 0xFF03DAC6,
- background: Long = 0xFF121212,
- surface: Long = 0xFF121212,
- error: Long = 0xFFCF6679,
- onPrimary: Long = 0xFF000000,
- onSecondary: Long = 0xFF000000,
- onBackground: Long = 0xFFFFFFFF,
- onSurface: Long = 0xFFFFFFFF,
- onError: Long = 0xFF000000
-) = darkColors(
- primary = Color(primary),
- primaryVariant = Color(primaryVariant),
- secondary = Color(secondary),
- background = Color(background),
- surface = Color(surface),
- error = Color(error),
- onPrimary = Color(onPrimary),
- onSecondary = Color(onSecondary),
- onBackground = Color(onBackground),
- onSurface = Color(onSurface),
- onError = Color(onError)
-)
\ No newline at end of file
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 858fa8d..902fe64 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
@@ -18,10 +18,15 @@
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -31,28 +36,31 @@
import androidx.compose.integration.demos.common.DemoCategory
import androidx.compose.integration.demos.common.FragmentDemo
import androidx.compose.integration.demos.common.allLaunchableDemos
-import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
-import androidx.compose.material.ListItem
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Scaffold
-import androidx.compose.material.Surface
-import androidx.compose.material.Text
-import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SmallTopAppBar
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.LayoutDirection
@@ -61,6 +69,7 @@
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentContainerView
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DemoApp(
currentDemo: Demo,
@@ -77,11 +86,14 @@
var filterText by rememberSaveable { mutableStateOf("") }
+ val scrollBehavior = remember { TopAppBarDefaults.pinnedScrollBehavior() }
+
Scaffold(
topBar = {
DemoAppBar(
title = backStackTitle,
- navigationIcon = navigationIcon,
+ scrollBehavior = scrollBehavior,
+ navigationIcon = navigationIcon ?: {},
launchSettings = launchSettings,
isFiltering = isFiltering,
filterText = filterText,
@@ -89,7 +101,8 @@
onStartFiltering = onStartFiltering,
onEndFiltering = onEndFiltering
)
- }
+ },
+ modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) { innerPadding ->
val modifier = Modifier.padding(innerPadding)
DemoContent(modifier, currentDemo, isFiltering, filterText, onNavigateToDemo, onNavigateUp)
@@ -106,7 +119,7 @@
onNavigateUp: () -> Unit
) {
Crossfade(isFiltering to currentDemo) { (filtering, demo) ->
- Surface(modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
+ Surface(modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
if (filtering) {
DemoFilter(
launchableDemos = AllDemosCategory.allLaunchableDemos(),
@@ -121,12 +134,26 @@
}
@Composable
+fun Material2LegacyTheme(content: @Composable () -> Unit) {
+ val material2Colors =
+ if (isSystemInDarkTheme()) {
+ androidx.compose.material.darkColors()
+ } else {
+ androidx.compose.material.lightColors()
+ }
+ androidx.compose.material.MaterialTheme(colors = material2Colors, content = content)
+}
+
+@Composable
private fun DisplayDemo(demo: Demo, onNavigate: (Demo) -> Unit, onNavigateUp: () -> Unit) {
when (demo) {
is ActivityDemo<*> -> {
/* should never get here as activity demos are not added to the backstack*/
}
- is ComposableDemo -> demo.content(onNavigateUp)
+ is ComposableDemo ->
+ // provide material 2 as well for interop, find a way to
+ // remove it when all demos migrated to m3
+ Material2LegacyTheme { demo.content(onNavigateUp) }
is DemoCategory -> DisplayDemoCategory(demo, onNavigate)
is FragmentDemo<*> -> {
lateinit var view: FragmentContainerView
@@ -156,21 +183,16 @@
}
@Composable
-@OptIn(ExperimentalMaterialApi::class)
private fun DisplayDemoCategory(category: DemoCategory, onNavigate: (Demo) -> Unit) {
- // TODO: migrate to LazyColumn after b/175671850
+ // TODO: migrate to LazyColumn after DemoTests are rewritten to accommodate laziness
Column(Modifier.verticalScroll(rememberScrollState())) {
category.demos.forEach { demo ->
- ListItem(
- text = {
- Text(
- modifier = Modifier.height(56.dp)
- .wrapContentSize(Alignment.Center),
- text = demo.title
- )
- },
- modifier = Modifier.clickable { onNavigate(demo) }
- )
+ ListItem(onClick = { onNavigate(demo) }) {
+ Text(
+ modifier = Modifier.height(56.dp).wrapContentSize(Alignment.Center),
+ text = demo.title
+ )
+ }
}
}
}
@@ -179,7 +201,8 @@
@Composable
private fun DemoAppBar(
title: String,
- navigationIcon: @Composable (() -> Unit)?,
+ scrollBehavior: TopAppBarScrollBehavior,
+ navigationIcon: @Composable () -> Unit,
isFiltering: Boolean,
filterText: String,
onFilter: (String) -> Unit,
@@ -191,13 +214,15 @@
FilterAppBar(
filterText = filterText,
onFilter = onFilter,
- onClose = onEndFiltering
+ onClose = onEndFiltering,
+ scrollBehavior = scrollBehavior
)
} else {
- TopAppBar(
+ SmallTopAppBar(
title = {
Text(title, Modifier.testTag(Tags.AppBarTitle))
},
+ scrollBehavior = scrollBehavior,
navigationIcon = navigationIcon,
actions = {
AppBarIcons.Filter(onClick = onStartFiltering)
@@ -233,3 +258,20 @@
}
}
}
+
+@Composable
+internal fun ListItem(
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ content: @Composable (() -> Unit)
+) {
+ Box(
+ modifier
+ .heightIn(min = 48.dp)
+ .fillMaxWidth()
+ .clickable(onClick = onClick)
+ .padding(horizontal = 16.dp)
+ .wrapContentHeight(Alignment.CenterVertically),
+ contentAlignment = Alignment.CenterStart
+ ) { content() }
+}
\ No newline at end of file
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoColors.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoColors.kt
deleted file mode 100644
index 5bb3529..0000000
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoColors.kt
+++ /dev/null
@@ -1,41 +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.integration.demos
-
-import android.content.SharedPreferences
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Stable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.material.Colors
-import androidx.compose.material.darkColors
-import androidx.compose.material.lightColors
-
-/**
- * Wrapper class that contains a light and dark [Colors], to allow saving and
- * restoring the entire light / dark theme to and from [SharedPreferences].
- */
-@Stable
-class DemoColors {
- var light: Colors by mutableStateOf(lightColors())
- var dark: Colors by mutableStateOf(darkColors())
-
- val colors
- @Composable get() = if (isSystemInDarkTheme()) dark else light
-}
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoFilter.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoFilter.kt
index 957232c..62a05db 100644
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoFilter.kt
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoFilter.kt
@@ -17,7 +17,6 @@
package androidx.compose.integration.demos
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -26,17 +25,16 @@
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.verticalScroll
import androidx.compose.integration.demos.common.Demo
-import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
-import androidx.compose.material.ListItem
-import androidx.compose.material.LocalContentColor
-import androidx.compose.material.LocalTextStyle
-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.Close
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.LocalTextStyle
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SmallTopAppBar
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.key
@@ -46,7 +44,6 @@
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
@@ -73,33 +70,30 @@
}
/**
- * [TopAppBar] with a text field allowing filtering all the demos.
+ * [SmallTopAppBar] with a text field allowing filtering all the demos.
*/
@Composable
fun FilterAppBar(
filterText: String,
onFilter: (String) -> Unit,
- onClose: () -> Unit
+ onClose: () -> Unit,
+ scrollBehavior: TopAppBarScrollBehavior
) {
- with(MaterialTheme.colors) {
- val appBarColor = if (isLight) {
- surface
- } else {
- // Blending primary over surface according to Material design guidance for brand
- // surfaces in dark theme
- primary.copy(alpha = 0.08f).compositeOver(surface)
- }
- TopAppBar(backgroundColor = appBarColor, contentColor = onSurface) {
- IconButton(modifier = Modifier.align(Alignment.CenterVertically), onClick = onClose) {
+ SmallTopAppBar(
+ navigationIcon = {
+ IconButton(onClick = onClose) {
Icon(Icons.Filled.Close, null)
}
+ },
+ title = {
FilterField(
filterText,
onFilter,
- Modifier.fillMaxWidth().align(Alignment.CenterVertically)
+ Modifier.fillMaxWidth()
)
- }
- }
+ },
+ scrollBehavior = scrollBehavior
+ )
}
/**
@@ -131,13 +125,12 @@
* [ListItem] that displays a [demo] and highlights any matches for [filterText] inside [Demo.title]
*/
@Composable
-@OptIn(ExperimentalMaterialApi::class)
private fun FilteredDemoListItem(
demo: Demo,
filterText: String,
onNavigate: (Demo) -> Unit
) {
- val primary = MaterialTheme.colors.primary
+ val primary = MaterialTheme.colorScheme.primary
val annotatedString = buildAnnotatedString {
val title = demo.title
var currentIndex = 0
@@ -159,13 +152,11 @@
}
key(demo.title) {
ListItem(
- text = {
- Text(
- modifier = Modifier.height(56.dp).wrapContentSize(Alignment.Center),
- text = annotatedString
- )
- },
- modifier = Modifier.clickable { onNavigate(demo) }
- )
+ onClick = { onNavigate(demo) }) {
+ Text(
+ modifier = Modifier.height(56.dp).wrapContentSize(Alignment.Center),
+ text = annotatedString
+ )
+ }
}
}
diff --git a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoSettingsActivity.kt b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoSettingsActivity.kt
index 3f0f540..e41e89c 100644
--- a/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoSettingsActivity.kt
+++ b/compose/integration-tests/demos/src/main/java/androidx/compose/integration/demos/DemoSettingsActivity.kt
@@ -17,21 +17,15 @@
package androidx.compose.integration.demos
import android.content.Context
-import android.content.SharedPreferences
+import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
-import androidx.preference.EditTextPreference
-import androidx.preference.Preference
+import androidx.compose.integration.demos.DemoSettingsActivity.SettingsFragment
+import androidx.preference.CheckBoxPreference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import androidx.preference.plusAssign
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
-import androidx.compose.material.Colors
-import androidx.compose.material.darkColors
-import androidx.compose.material.lightColors
-import kotlin.reflect.full.memberProperties
/**
* Shell [AppCompatActivity] around [SettingsFragment], as we need a FragmentActivity subclass
@@ -56,371 +50,22 @@
screen += this
}
- general += Preference(context).apply {
- title = "Shuffle all colors"
- onPreferenceClickListener = Preference.OnPreferenceClickListener {
- generateRandomPalette().saveColors(context)
- requireActivity().finish()
- true
- }
- }
-
- general += Preference(context).apply {
- title = "Reset colors to default"
- onPreferenceClickListener = Preference.OnPreferenceClickListener {
- val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
- sharedPreferences.edit().clear().apply()
- requireActivity().finish()
- true
- }
- }
-
- val light = PreferenceCategory(context).apply {
- title = "Light colors"
- screen += this
- }
- // Create new Colors to resolve defaults
- lightColors().forEachColorProperty { name, color ->
- light += EditTextPreference(context).apply {
- key = name + true
- title = name
- // set the default value to be the default for Colors
- setDefaultValue(Integer.toHexString(color.toArgb()))
- summaryProvider = EditTextPreference.SimpleSummaryProvider.getInstance()
- }
- }
-
- val dark = PreferenceCategory(context).apply {
- title = "Dark colors"
- screen += this
- }
-
- darkColors().forEachColorProperty { name, color ->
- dark += EditTextPreference(context).apply {
- key = name + false
- title = name
- // set the default value to be the default for Colors
- setDefaultValue(Integer.toHexString(color.toArgb()))
- summaryProvider = EditTextPreference.SimpleSummaryProvider.getInstance()
- }
+ general += CheckBoxPreference(context).apply {
+ title = "Dynamic theming (android S+)"
+ isEnabled = IsDynamicThemingAvailable
+ key = IsDynamicThemeOnKey
+ setDefaultValue(isDynamicThemeSettingOn(context))
}
preferenceScreen = screen
}
}
}
-/**
- * Iterates over each color present in a given [Colors].
- *
- * @param action the action to take on each property, where name is the name of the property,
- * such as 'primary' for [Colors.primary], and color is the resolved [Color] of the
- * property.
- */
-private fun Colors.forEachColorProperty(action: (name: String, color: Color) -> Unit) {
- Colors::class.memberProperties.forEach { property ->
- val name = property.name
- val color = property.get(this) as? Color ?: return@forEach
- action(name, color)
- }
+internal fun isDynamicThemeSettingOn(context: Context): Boolean {
+ return PreferenceManager
+ .getDefaultSharedPreferences(context)
+ .getBoolean(IsDynamicThemeOnKey, IsDynamicThemingAvailable)
}
-/**
- * Persists the current [DemoColors] to [SharedPreferences].
- */
-private fun DemoColors.saveColors(context: Context) {
- light.forEachColorProperty { name, color ->
- PreferenceManager.getDefaultSharedPreferences(context)
- .edit()
- .putString(name + true, Integer.toHexString(color.toArgb()))
- .apply()
- }
- dark.forEachColorProperty { name, color ->
- PreferenceManager.getDefaultSharedPreferences(context)
- .edit()
- .putString(name + false, Integer.toHexString(color.toArgb()))
- .apply()
- }
-}
-
-/**
- * Generates a [DemoColors] with random [Colors.primary], [Colors.onPrimary],
- * [Colors.secondary] and [Colors.onSecondary] as random dark-on-light or light-on-dark
- * pairs. Other colors are kept from the baseline.
- */
-private fun generateRandomPalette(): DemoColors {
- return DemoColors().apply {
- val (lightPrimary, lightOnPrimary) = generateColorPair(true)
- val (lightSecondary, lightOnSecondary) = generateColorPair(true)
- light = lightColors(
- primary = lightPrimary,
- secondary = lightSecondary,
- onPrimary = lightOnPrimary,
- onSecondary = lightOnSecondary
- )
- val (darkPrimary, darkOnPrimary) = generateColorPair(false)
- val (darkSecondary, darkOnSecondary) = generateColorPair(false)
- dark = darkColors(
- primary = darkPrimary,
- secondary = darkSecondary,
- onPrimary = darkOnPrimary,
- onSecondary = darkOnSecondary
- )
- }
-}
-
-/**
- * Generate a random dark and light color from the palette, and returns either a dark-on-light
- * or light-on-dark color pair.
- */
-private fun generateColorPair(isLightTheme: Boolean): Pair<Color, Color> {
- val darkColor = Color(DarkPaletteColors.random())
- val lightColor = Color(LightPaletteColors.random())
- return if (isLightTheme) {
- darkColor to lightColor
- } else {
- lightColor to darkColor
- }
-}
-
-// Colors taken from https://material.io/design/color -> 2014 Material Design color palettes
-val LightPaletteColors = listOf(
- 0xFFEF5350,
- 0xFFF44336,
- 0xFFE53935,
- 0xFFD32F2F,
- 0xFFC62828,
- 0xFFB71C1C,
- 0xFFFF5252,
- 0xFFFF1744,
- 0xFFD50000,
- 0xFFEC407A,
- 0xFFE91E63,
- 0xFFD81B60,
- 0xFFC2185B,
- 0xFFAD1457,
- 0xFF880E4F,
- 0xFFFF4081,
- 0xFFF50057,
- 0xFFC51162,
- 0xFFBA68C8,
- 0xFFAB47BC,
- 0xFF9C27B0,
- 0xFF8E24AA,
- 0xFF7B1FA2,
- 0xFF6A1B9A,
- 0xFF4A148C,
- 0xFFE040FB,
- 0xFFD500F9,
- 0xFFAA00FF,
- 0xFF9575CD,
- 0xFF7E57C2,
- 0xFF673AB7,
- 0xFF5E35B1,
- 0xFF512DA8,
- 0xFF4527A0,
- 0xFF311B92,
- 0xFF7C4DFF,
- 0xFF651FFF,
- 0xFF6200EA,
- 0xFF7986CB,
- 0xFF5C6BC0,
- 0xFF3F51B5,
- 0xFF3949AB,
- 0xFF303F9F,
- 0xFF283593,
- 0xFF1A237E,
- 0xFF536DFE,
- 0xFF3D5AFE,
- 0xFF304FFE,
- 0xFF1E88E5,
- 0xFF1976D2,
- 0xFF1565C0,
- 0xFF0D47A1,
- 0xFF448AFF,
- 0xFF2979FF,
- 0xFF2962FF,
- 0xFF0288D1,
- 0xFF0277BD,
- 0xFF01579B,
- 0xFF0091EA,
- 0xFF0097A7,
- 0xFF00838F,
- 0xFF006064,
- 0xFF009688,
- 0xFF00897B,
- 0xFF00796B,
- 0xFF00695C,
- 0xFF004D40,
- 0xFF43A047,
- 0xFF388E3C,
- 0xFF2E7D32,
- 0xFF1B5E20,
- 0xFF558B2F,
- 0xFF33691E,
- 0xFF827717,
- 0xFFE65100,
- 0xFFF4511E,
- 0xFFE64A19,
- 0xFFD84315,
- 0xFFBF360C,
- 0xFFFF3D00,
- 0xFFDD2C00,
- 0xFFA1887F,
- 0xFF8D6E63,
- 0xFF795548,
- 0xFF6D4C41,
- 0xFF5D4037,
- 0xFF4E342E,
- 0xFF3E2723,
- 0xFF757575,
- 0xFF616161,
- 0xFF424242,
- 0xFF212121,
- 0xFF78909C,
- 0xFF607D8B,
- 0xFF546E7A,
- 0xFF455A64,
- 0xFF37474F,
- 0xFF263238
-)
-
-val DarkPaletteColors = listOf(
- 0xFFFFCDD2,
- 0xFFEF9A9A,
- 0xFFE57373,
- 0xFFFF8A80,
- 0xFFF8BBD0,
- 0xFFF48FB1,
- 0xFFF06292,
- 0xFFFF80AB,
- 0xFFE1BEE7,
- 0xFFCE93D8,
- 0xFFEA80FC,
- 0xFFD1C4E9,
- 0xFFB39DDB,
- 0xFFB388FF,
- 0xFFC5CAE9,
- 0xFF9FA8DA,
- 0xFF8C9EFF,
- 0xFFBBDEFB,
- 0xFF90CAF9,
- 0xFF64B5F6,
- 0xFF42A5F5,
- 0xFF2196F3,
- 0xFF82B1FF,
- 0xFFB3E5FC,
- 0xFF81D4FA,
- 0xFF4FC3F7,
- 0xFF29B6F6,
- 0xFF03A9F4,
- 0xFF039BE5,
- 0xFF80D8FF,
- 0xFF40C4FF,
- 0xFF00B0FF,
- 0xFFB2EBF2,
- 0xFF80DEEA,
- 0xFF4DD0E1,
- 0xFF26C6DA,
- 0xFF00BCD4,
- 0xFF00ACC1,
- 0xFF84FFFF,
- 0xFF18FFFF,
- 0xFF00E5FF,
- 0xFF00B8D4,
- 0xFFB2DFDB,
- 0xFF80CBC4,
- 0xFF4DB6AC,
- 0xFF26A69A,
- 0xFFA7FFEB,
- 0xFF64FFDA,
- 0xFF1DE9B6,
- 0xFF00BFA5,
- 0xFFC8E6C9,
- 0xFFA5D6A7,
- 0xFF81C784,
- 0xFF66BB6A,
- 0xFF4CAF50,
- 0xFFB9F6CA,
- 0xFF69F0AE,
- 0xFF00E676,
- 0xFF00C853,
- 0xFFDCEDC8,
- 0xFFC5E1A5,
- 0xFFAED581,
- 0xFF9CCC65,
- 0xFF8BC34A,
- 0xFF7CB342,
- 0xFF689F38,
- 0xFFCCFF90,
- 0xFFB2FF59,
- 0xFF76FF03,
- 0xFF64DD17,
- 0xFFF0F4C3,
- 0xFFE6EE9C,
- 0xFFDCE775,
- 0xFFD4E157,
- 0xFFCDDC39,
- 0xFFC0CA33,
- 0xFFAFB42B,
- 0xFF9E9D24,
- 0xFFF4FF81,
- 0xFFEEFF41,
- 0xFFC6FF00,
- 0xFFAEEA00,
- 0xFFFFF9C4,
- 0xFFFFF59D,
- 0xFFFFF176,
- 0xFFFFEE58,
- 0xFFFFEB3B,
- 0xFFFDD835,
- 0xFFFBC02D,
- 0xFFF9A825,
- 0xFFF57F17,
- 0xFFFFFF8D,
- 0xFFFFFF00,
- 0xFFFFEA00,
- 0xFFFFD600,
- 0xFFFFECB3,
- 0xFFFFE082,
- 0xFFFFD54F,
- 0xFFFFCA28,
- 0xFFFFC107,
- 0xFFFFB300,
- 0xFFFFA000,
- 0xFFFF8F00,
- 0xFFFF6F00,
- 0xFFFFE57F,
- 0xFFFFD740,
- 0xFFFFC400,
- 0xFFFFAB00,
- 0xFFFFE0B2,
- 0xFFFFCC80,
- 0xFFFFB74D,
- 0xFFFFA726,
- 0xFFFF9800,
- 0xFFFB8C00,
- 0xFFF57C00,
- 0xFFEF6C00,
- 0xFFFFD180,
- 0xFFFFAB40,
- 0xFFFF9100,
- 0xFFFF6D00,
- 0xFFFFCCBC,
- 0xFFFFAB91,
- 0xFFFF8A65,
- 0xFFFF7043,
- 0xFFFF5722,
- 0xFFFF9E80,
- 0xFFFF6E40,
- 0xFFD7CCC8,
- 0xFFBCAAA4,
- 0xFFF5F5F5,
- 0xFFEEEEEE,
- 0xFFE0E0E0,
- 0xFFBDBDBD,
- 0xFF9E9E9E,
- 0xFFCFD8DC,
- 0xFFB0BEC5,
- 0xFF90A4AE,
- 0xFFFFFFFF
-)
\ No newline at end of file
+private const val IsDynamicThemeOnKey = "material3_isDynamicThemeOn"
+internal val IsDynamicThemingAvailable = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
\ No newline at end of file
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/InteropUi.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/InteropUi.kt
index 960a3c3..1ef5eae 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/InteropUi.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/InteropUi.kt
@@ -34,6 +34,7 @@
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
@@ -43,6 +44,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -181,7 +183,27 @@
}
}
-private object InteropUiSnippet6 {
+@Composable
+fun InteropUiSnippet6(showCautionIcon: Boolean) {
+ if (showCautionIcon) {
+ CautionIcon(/* ... */)
+ }
+}
+
+@Composable
+fun InteropUiSnippet7() {
+ var isEnabled by rememberSaveable { mutableStateOf(false) }
+
+ Column {
+ ImageWithEnabledOverlay(isEnabled)
+ ControlPanelWithToggle(
+ isEnabled = isEnabled,
+ onEnabledChanged = { isEnabled = it }
+ )
+ }
+}
+
+private object InteropUiSnippet8 {
@Composable
fun MyComposable() {
BoxWithConstraints {
@@ -196,7 +218,7 @@
}
}
-private object InteropUiSnippet7 {
+private object InteropUiSnippet9 {
// import androidx.compose.ui.platform.ComposeView
class MyComposeAdapter : RecyclerView.Adapter<MyComposeViewHolder>() {
@@ -233,7 +255,7 @@
}
}
-private object InteropUiSnippet8 {
+private object InteropUiSnippet10 {
// import androidx.compose.ui.platform.ViewCompositionStrategy
class MyComposeViewHolder(
@@ -295,6 +317,21 @@
private fun Icon() {
}
+@Composable
+private fun CautionIcon() {
+}
+
+@Composable
+private fun ImageWithEnabledOverlay(isEnabled: Boolean) {
+}
+
+@Composable
+private fun ControlPanelWithToggle(
+ isEnabled: Boolean,
+ onEnabledChanged: (Boolean) -> Unit
+) {
+}
+
private class WindowCompat {
companion object {
fun setDecorFitsSystemWindows(window: Any, bool: Boolean) {}
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
index 82f2c4a..ac91305 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/interoperability/Interoperability.kt
@@ -35,8 +35,8 @@
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
+import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
-import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.integration.docs.databinding.ExampleLayoutBinding
@@ -53,6 +53,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.viewinterop.AndroidViewBinding
import androidx.lifecycle.MutableLiveData
@@ -66,7 +67,7 @@
*/
private object InteropSnippet1 {
- class ExampleActivity : AppCompatActivity() {
+ class ExampleActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -87,22 +88,34 @@
private object InteropSnippet2 {
class ExampleFragment : Fragment() {
+ private var _binding: FragmentExampleBinding? = null
+ // This property is only valid between onCreateView and onDestroyView.
+ private val binding get() = _binding!!
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
- // Inflate the layout for this fragment
- return inflater.inflate(
- R.layout.fragment_example, container, false
- ).apply {
- findViewById<ComposeView>(R.id.compose_view).setContent {
+ _binding = FragmentExampleBinding.inflate(inflater, container, false)
+ val view = binding.root
+ binding.composeView.apply {
+ // Dispose of the Composition when the view's LifecycleOwner
+ // is destroyed
+ setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
+ setContent {
// In Compose world
MaterialTheme {
Text("Hello Compose!")
}
}
}
+ return view
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
}
}
}
@@ -116,6 +129,9 @@
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
+ // Dispose of the Composition when the view's LifecycleOwner
+ // is destroyed
+ setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
// In Compose world
@@ -206,7 +222,7 @@
/* ktlint-disable indent */
private object InteropSnippet8 {
- class ExampleActivity : AppCompatActivity() {
+ class ExampleActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// get data from savedInstanceState
@@ -228,7 +244,7 @@
}
}
-private object InteropSnippet19 {
+private object InteropSnippet9 {
@Composable
fun SystemBroadcastReceiver(
systemAction: String,
@@ -347,4 +363,18 @@
}
fun requireContext(): Context = TODO()
+
+ open fun onDestroyView() { }
+}
+
+private class FragmentExampleBinding {
+ val root: View = TODO()
+ var composeView: ComposeView
+ companion object {
+ fun inflate(
+ li: LayoutInflater,
+ container: ViewGroup?,
+ boolean: Boolean
+ ): FragmentExampleBinding { TODO() }
+ }
}
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt
index c66d0c3..d317818 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/resources/Resources.kt
@@ -25,6 +25,8 @@
import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Divider
import androidx.compose.material.Icon
@@ -136,10 +138,10 @@
// - res/drawable/animated_vector.xml
// In your Compose code
- val image = animatedVectorResource(id = R.drawable.animated_vector)
+ val image = AnimatedImageVector.animatedVectorResource(id = R.drawable.animated_vector)
val atEnd by remember { mutableStateOf(false) }
Icon(
- painter = image.painterFor(atEnd = atEnd),
+ painter = rememberAnimatedVectorPainter(image, atEnd),
contentDescription = null // decorative element
)
}
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/sideeffects/SideEffects.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/sideeffects/SideEffects.kt
index d5a5c46..5c703e0 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/sideeffects/SideEffects.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/sideeffects/SideEffects.kt
@@ -238,7 +238,7 @@
// Calculate high priority tasks only when the todoTasks or highPriorityKeywords
// change, not on every recomposition
- val highPriorityTasks by remember(todoTasks, highPriorityKeywords) {
+ val highPriorityTasks by remember {
derivedStateOf { todoTasks.filter { it.containsWord(highPriorityKeywords) } }
}
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
index 9e17c64..a8ed7dec2 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/state/State.kt
@@ -221,7 +221,7 @@
// Logic to decide when to show the bottom bar
val shouldShowBottomBar: Boolean
- @Composable get() = /* ... */
+ get() = /* ... */
// DO NOT COPY IN DAC
false
diff --git a/compose/lint/internal-lint-checks/build.gradle b/compose/lint/internal-lint-checks/build.gradle
index 412cfb9..3039214 100644
--- a/compose/lint/internal-lint-checks/build.gradle
+++ b/compose/lint/internal-lint-checks/build.gradle
@@ -25,7 +25,6 @@
dependencies {
compileOnly(libs.androidLintApi)
compileOnly(libs.kotlinStdlib)
- api(project(":lint-checks"))
implementation(project(":compose:lint:common"))
testImplementation(project(":compose:lint:common-test"))
diff --git a/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/ComposeIssueRegistry.kt b/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/ComposeIssueRegistry.kt
index 89745e0..3bce32a 100644
--- a/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/ComposeIssueRegistry.kt
+++ b/compose/lint/internal-lint-checks/src/main/java/androidx/compose/lint/ComposeIssueRegistry.kt
@@ -18,7 +18,6 @@
package androidx.compose.lint
-import androidx.build.lint.AndroidXIssueRegistry
import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
@@ -32,7 +31,7 @@
ListIteratorDetector.ISSUE,
ModifierInspectorInfoDetector.ISSUE,
UnnecessaryLambdaCreationDetector.ISSUE,
- ) + AndroidXIssueRegistry.Issues
+ )
}
override val vendor = Vendor(
vendorName = "Jetpack Compose",
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderScreenshotTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderScreenshotTest.kt
index dfbdacc..b282d58 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderScreenshotTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SliderScreenshotTest.kt
@@ -223,4 +223,27 @@
}
assertSliderAgainstGolden("rangeSlider_fullRange")
}
+
+ @Test
+ @ExperimentalMaterialApi
+ fun rangeSliderTest_steps_customColors() {
+ rule.setMaterialContent {
+ Box(wrap.testTag(wrapperTestTag)) {
+ var position by remember { mutableStateOf(30f..70f) }
+ RangeSlider(
+ values = position,
+ valueRange = 0f..100f,
+ onValueChange = { position = it }, steps = 9,
+ colors = SliderDefaults.colors(
+ thumbColor = Color.Blue,
+ activeTrackColor = Color.Red,
+ inactiveTrackColor = Color.Yellow,
+ activeTickColor = Color.Magenta,
+ inactiveTickColor = Color.Cyan
+ )
+ )
+ }
+ }
+ assertSliderAgainstGolden("rangeSlider_steps_customColors")
+ }
}
\ No newline at end of file
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt
index 49995ca..e0d0a92 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt
@@ -54,6 +54,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import kotlin.math.floor
+import kotlin.math.max
/**
* <a href="https://material.io/components/checkboxes" class="external" target="_blank">Material Design checkbox</a>.
@@ -318,20 +319,29 @@
val halfStrokeWidth = strokeWidth / 2.0f
val stroke = Stroke(strokeWidth)
val checkboxSize = size.width
- drawRoundRect(
- boxColor,
- topLeft = Offset(strokeWidth, strokeWidth),
- size = Size(checkboxSize - strokeWidth * 2, checkboxSize - strokeWidth * 2),
- cornerRadius = CornerRadius(radius / 2),
- style = Fill
- )
- drawRoundRect(
- borderColor,
- topLeft = Offset(halfStrokeWidth, halfStrokeWidth),
- size = Size(checkboxSize - strokeWidth, checkboxSize - strokeWidth),
- cornerRadius = CornerRadius(radius),
- style = stroke
- )
+ if (boxColor == borderColor) {
+ drawRoundRect(
+ boxColor,
+ size = Size(checkboxSize, checkboxSize),
+ cornerRadius = CornerRadius(radius),
+ style = Fill
+ )
+ } else {
+ drawRoundRect(
+ boxColor,
+ topLeft = Offset(strokeWidth, strokeWidth),
+ size = Size(checkboxSize - strokeWidth * 2, checkboxSize - strokeWidth * 2),
+ cornerRadius = CornerRadius(max(0f, radius - strokeWidth)),
+ style = Fill
+ )
+ drawRoundRect(
+ borderColor,
+ topLeft = Offset(halfStrokeWidth, halfStrokeWidth),
+ size = Size(checkboxSize - strokeWidth, checkboxSize - strokeWidth),
+ cornerRadius = CornerRadius(radius - halfStrokeWidth),
+ style = stroke
+ )
+ }
}
private fun DrawScope.drawCheck(
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 d7ad38c..4b99271 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
@@ -678,17 +678,18 @@
trackStrokeWidth,
StrokeCap.Round
)
- tickFractions.groupBy { it > positionFractionEnd }.forEach { (afterFraction, list) ->
- drawPoints(
- list.map {
- Offset(lerp(sliderStart, sliderEnd, it).x, center.y)
- },
- PointMode.Points,
- (if (afterFraction) inactiveTickColor else activeTickColor).value,
- trackStrokeWidth,
- StrokeCap.Round
- )
- }
+ tickFractions.groupBy { it > positionFractionEnd || it < positionFractionStart }
+ .forEach { (outsideFraction, list) ->
+ drawPoints(
+ list.map {
+ Offset(lerp(sliderStart, sliderEnd, it).x, center.y)
+ },
+ PointMode.Points,
+ (if (outsideFraction) inactiveTickColor else activeTickColor).value,
+ trackStrokeWidth,
+ StrokeCap.Round
+ )
+ }
}
}
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index ce0c80e..5c90c17 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -305,14 +305,7 @@
}
public final class ScaffoldKt {
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Scaffold(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.ScaffoldState scaffoldState, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? drawerContent, optional boolean drawerGesturesEnabled, optional androidx.compose.ui.graphics.Shape drawerShape, optional float drawerTonalElevation, optional long drawerContainerColor, optional long drawerContentColor, optional long drawerScrimColor, optional long containerColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
- method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static androidx.compose.material3.ScaffoldState rememberScaffoldState(optional androidx.compose.material3.DrawerState drawerState);
- }
-
- @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Stable public final class ScaffoldState {
- ctor public ScaffoldState(androidx.compose.material3.DrawerState drawerState);
- method public androidx.compose.material3.DrawerState getDrawerState();
- property public final androidx.compose.material3.DrawerState drawerState;
+ method @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void Scaffold(optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> topBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> bottomBar, optional kotlin.jvm.functions.Function0<kotlin.Unit> floatingActionButton, optional int floatingActionButtonPosition, optional long containerColor, optional long contentColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.PaddingValues,kotlin.Unit> content);
}
public final class Strings_androidKt {
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ScaffoldSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ScaffoldSamples.kt
new file mode 100644
index 0000000..e0eb904
--- /dev/null
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ScaffoldSamples.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Menu
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExtendedFloatingActionButton
+import androidx.compose.material3.FabPosition
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SmallTopAppBar
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Sampled
+@Composable
+fun SimpleScaffoldWithTopBar() {
+ val colors = listOf(
+ Color(0xFFffd7d7.toInt()),
+ Color(0xFFffe9d6.toInt()),
+ Color(0xFFfffbd0.toInt()),
+ Color(0xFFe3ffd9.toInt()),
+ Color(0xFFd0fff8.toInt())
+ )
+
+ Scaffold(
+ topBar = {
+ SmallTopAppBar(
+ title = { Text("Simple Scaffold Screen") },
+ navigationIcon = {
+ IconButton(
+ onClick = { /* "Open nav drawer" */ }
+ ) {
+ Icon(Icons.Filled.Menu, contentDescription = "Localized description")
+ }
+ }
+ )
+ },
+ floatingActionButtonPosition = FabPosition.End,
+ floatingActionButton = {
+ ExtendedFloatingActionButton(
+ text = { Text("Inc") },
+ onClick = { /* fab click handler */ }
+ )
+ },
+ content = { innerPadding ->
+ LazyColumn(contentPadding = innerPadding) {
+ items(count = 100) {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .height(50.dp)
+ .background(colors[it % colors.size])
+ )
+ }
+ }
+ }
+ )
+}
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/IconButtonScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/IconButtonScreenshotTest.kt
new file mode 100644
index 0000000..67f6d1e
--- /dev/null
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/IconButtonScreenshotTest.kt
@@ -0,0 +1,177 @@
+/*
+ * 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.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusProperties
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.hasClickAction
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performMouseInput
+import androidx.compose.ui.test.performTouchInput
+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.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+@OptIn(ExperimentalTestApi::class)
+class IconButtonScreenshotTest {
+
+ @get:Rule
+ val rule = createComposeRule()
+
+ @get:Rule
+ val screenshotRule = AndroidXScreenshotTestRule(GOLDEN_MATERIAL3)
+
+ private val wrap = Modifier.wrapContentSize(Alignment.TopStart)
+ private val wrapperTestTag = "iconButtonWrapper"
+
+ @Test
+ fun iconButton_lightTheme() {
+ rule.setMaterialContent {
+ Box(wrap.testTag(wrapperTestTag)) {
+ IconButton(onClick = { /* doSomething() */ }) {
+ Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
+ }
+ }
+ }
+ assertAgainstGolden("iconButton_lightTheme")
+ }
+
+ @Test
+ fun iconButton_darkTheme() {
+ rule.setContent {
+ MaterialTheme(darkColorScheme()) {
+ Surface(modifier = Modifier.fillMaxSize()) {
+ Box(wrap.testTag(wrapperTestTag)) {
+ IconButton(onClick = { /* doSomething() */ }) {
+ Icon(
+ Icons.Filled.Favorite,
+ contentDescription = "Localized description"
+ )
+ }
+ }
+ }
+ }
+ }
+ assertAgainstGolden("iconButton_darkTheme")
+ }
+
+ @Test
+ fun iconButton_lightTheme_disabled() {
+
+ rule.setMaterialContent {
+ Box(wrap.testTag(wrapperTestTag)) {
+ IconButton(onClick = { /* doSomething() */ }, enabled = false) {
+ Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
+ }
+ }
+ }
+ assertAgainstGolden("iconButton_lightTheme_disabled")
+ }
+
+ @Test
+ fun iconButton_lightTheme_pressed() {
+ rule.setMaterialContent {
+ Box(wrap.testTag(wrapperTestTag)) {
+ IconButton(onClick = { /* doSomething() */ }) {
+ Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
+ }
+ }
+ }
+
+ rule.mainClock.autoAdvance = false
+ rule.onNode(hasClickAction())
+ .performTouchInput { down(center) }
+
+ rule.mainClock.advanceTimeByFrame()
+ rule.waitForIdle() // Wait for measure
+ rule.mainClock.advanceTimeBy(milliseconds = 200)
+
+ // Ripples are drawn on the RenderThread, not the main (UI) thread, so we can't wait for
+ // synchronization. Instead just wait until after the ripples are finished animating.
+ Thread.sleep(300)
+
+ assertAgainstGolden("iconButton_lightTheme_pressed")
+ }
+
+ @Test
+ fun iconButton_lightTheme_hovered() {
+ rule.setMaterialContent {
+ Box(wrap.testTag(wrapperTestTag)) {
+ IconButton(onClick = { /* doSomething() */ }) {
+ Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
+ }
+ }
+ }
+ rule.onNodeWithTag(wrapperTestTag).performMouseInput {
+ enter(center)
+ }
+
+ assertAgainstGolden("iconButton_lightTheme_hovered")
+ }
+
+ @Test
+ fun iconButton_lightTheme_focused() {
+ val focusRequester = FocusRequester()
+
+ rule.setMaterialContent {
+ Box(wrap.testTag(wrapperTestTag)) {
+ IconButton(onClick = { /* doSomething() */ },
+ modifier = Modifier
+ // Normally this is only focusable in non-touch mode, so let's force it to
+ // always be focusable so we can test how it appears
+ .focusProperties { canFocus = true }
+ .focusRequester(focusRequester)
+ ) {
+ Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ focusRequester.requestFocus()
+ }
+
+ assertAgainstGolden("iconButton_lightTheme_focused")
+ }
+
+ private fun assertAgainstGolden(goldenName: String) {
+ rule.onNodeWithTag(wrapperTestTag)
+ .captureToImage()
+ .assertAgainstGolden(screenshotRule, goldenName)
+ }
+}
\ No newline at end of file
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
index ed666cb..1aee644 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
@@ -55,6 +55,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
import kotlin.math.floor
+import kotlin.math.max
/**
* Material Design checkbox.
@@ -337,20 +338,29 @@
val halfStrokeWidth = strokeWidth / 2.0f
val stroke = Stroke(strokeWidth)
val checkboxSize = size.width
- drawRoundRect(
- boxColor,
- topLeft = Offset(strokeWidth, strokeWidth),
- size = Size(checkboxSize - strokeWidth * 2, checkboxSize - strokeWidth * 2),
- cornerRadius = CornerRadius(radius / 2),
- style = Fill
- )
- drawRoundRect(
- borderColor,
- topLeft = Offset(halfStrokeWidth, halfStrokeWidth),
- size = Size(checkboxSize - strokeWidth, checkboxSize - strokeWidth),
- cornerRadius = CornerRadius(radius),
- style = stroke
- )
+ if (boxColor == borderColor) {
+ drawRoundRect(
+ boxColor,
+ size = Size(checkboxSize, checkboxSize),
+ cornerRadius = CornerRadius(radius),
+ style = Fill
+ )
+ } else {
+ drawRoundRect(
+ boxColor,
+ topLeft = Offset(strokeWidth, strokeWidth),
+ size = Size(checkboxSize - strokeWidth * 2, checkboxSize - strokeWidth * 2),
+ cornerRadius = CornerRadius(max(0f, radius - strokeWidth)),
+ style = Fill
+ )
+ drawRoundRect(
+ borderColor,
+ topLeft = Offset(halfStrokeWidth, halfStrokeWidth),
+ size = Size(checkboxSize - strokeWidth, checkboxSize - strokeWidth),
+ cornerRadius = CornerRadius(radius - halfStrokeWidth),
+ style = stroke
+ )
+ }
}
private fun DrawScope.drawCheck(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IconButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IconButton.kt
index ba9daf7..cafed40 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IconButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/IconButton.kt
@@ -70,7 +70,10 @@
enabled = enabled,
role = Role.Button,
interactionSource = interactionSource,
- indication = rememberRipple(bounded = false, radius = RippleRadius)
+ indication = rememberRipple(
+ bounded = false,
+ radius = IconButton.StateLayerSize / 2
+ )
),
contentAlignment = Alignment.Center
) {
@@ -121,7 +124,10 @@
enabled = enabled,
role = Role.Checkbox,
interactionSource = interactionSource,
- indication = rememberRipple(bounded = false, radius = RippleRadius)
+ indication = rememberRipple(
+ bounded = false,
+ radius = IconButton.StateLayerSize / 2
+ )
),
contentAlignment = Alignment.Center
) {
@@ -134,6 +140,3 @@
CompositionLocalProvider(LocalContentColor provides contentColor, content = content)
}
}
-
-// Default radius of an unbounded ripple in an IconButton
-private val RippleRadius = IconButton.StateLayerSize
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Scaffold.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Scaffold.kt
index 8609e2f..8c618ca 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Scaffold.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Scaffold.kt
@@ -16,21 +16,14 @@
package androidx.compose.material3
-import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.tokens.NavigationDrawer
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.Stable
-import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.SubcomposeLayout
-import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
@@ -43,26 +36,17 @@
* screen, by ensuring proper layout strategy for them and collecting necessary data so these
* components will work together correctly.
*
+ * Simple example of a Scaffold with [SmallTopAppBar], [FloatingActionButton]:
+ *
+ * @sample androidx.compose.material3.samples.SimpleScaffoldWithTopBar
+ *
* @param modifier optional Modifier for the root of the [Scaffold]
- * @param scaffoldState state of this scaffold widget. It contains the state of the screen, e.g.
- * variables to provide manual control over the drawer behavior, sizes of components, etc
* @param topBar top app bar of the screen. Consider using [SmallTopAppBar].
* @param bottomBar bottom bar of the screen. Consider using [NavigationBar].
* @param floatingActionButton Main action button of your screen. Consider using
* [FloatingActionButton] for this slot.
* @param floatingActionButtonPosition position of the FAB on the screen. See [FabPosition] for
* possible options available.
- * @param drawerContent content of the Drawer sheet that can be pulled from the left side (right
- * for RTL).
- * @param drawerGesturesEnabled whether or not drawer (if set) can be interacted with via gestures
- * @param drawerShape shape of the drawer sheet (if set)
- * @param drawerTonalElevation Affects the alpha of the color overlay applied on the container color
- * of the drawer sheet.
- * @param drawerContainerColor container color to be used for the drawer sheet
- * @param drawerContentColor color of the content to use inside the drawer sheet. Defaults to
- * either the matching content color for [drawerContainerColor], or, if it is not a color from
- * the theme, this will keep the same value set above this Surface.
- * @param drawerScrimColor color of the scrim that obscures content when the drawer is open
* @param containerColor background of the scaffold body
* @param contentColor color of the content in scaffold body. Defaults to either the matching
* content color for [containerColor], or, if it is not a color from the theme, this will keep
@@ -72,56 +56,27 @@
* you're using Modifier.VerticalScroll, apply this modifier to the child of the scroll, and not on
* the scroll itself.
*/
-// TODO(b/198144133): Add Simple example of a Scaffold with [SmallTopAppBar], [FloatingActionButton]
-// and [Navigation drawer].
@ExperimentalMaterial3Api
@Composable
fun Scaffold(
modifier: Modifier = Modifier,
- scaffoldState: ScaffoldState = rememberScaffoldState(),
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
- drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
- drawerGesturesEnabled: Boolean = true,
- drawerShape: Shape = RoundedCornerShape(16.dp),
- drawerTonalElevation: Dp = DrawerDefaults.Elevation,
- drawerContainerColor: Color =
- MaterialTheme.colorScheme.fromToken(NavigationDrawer.ContainerColor),
- drawerContentColor: Color = contentColorFor(drawerContainerColor),
- drawerScrimColor: Color = DrawerDefaults.scrimColor,
containerColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = contentColorFor(containerColor),
content: @Composable (PaddingValues) -> Unit
) {
- val child = @Composable { childModifier: Modifier ->
- Surface(modifier = childModifier, color = containerColor, contentColor = contentColor) {
- ScaffoldLayout(
- fabPosition = floatingActionButtonPosition,
- topBar = topBar,
- bottomBar = bottomBar,
- content = content,
- fab = floatingActionButton
- )
- }
- }
- if (drawerContent != null) {
- NavigationDrawer(
- modifier = modifier,
- drawerState = scaffoldState.drawerState,
- gesturesEnabled = drawerGesturesEnabled,
- drawerContent = drawerContent,
- drawerShape = drawerShape,
- drawerTonalElevation = drawerTonalElevation,
- drawerContainerColor = drawerContainerColor,
- drawerContentColor = drawerContentColor,
- scrimColor = drawerScrimColor,
- content = { child(Modifier) }
+ Surface(modifier = modifier, color = containerColor, contentColor = contentColor) {
+ ScaffoldLayout(
+ fabPosition = floatingActionButtonPosition,
+ topBar = topBar,
+ bottomBar = bottomBar,
+ content = content,
+ fab = floatingActionButton
)
- } else {
- child(modifier)
}
}
@@ -234,33 +189,6 @@
}
/**
- * State for [Scaffold] composable component.
- *
- * Contains basic screen state, e.g. Drawer configuration, as well as sizes of components after
- * layout has happened
- *
- * @param drawerState the drawer state
- */
-@Stable
-@ExperimentalMaterial3Api
-class ScaffoldState(
- val drawerState: DrawerState,
-)
-
-/**
- * Creates a [ScaffoldState] with the default animation clock and memoizes it.
- *
- * @param drawerState the drawer state
- */
-@ExperimentalMaterial3Api
-@Composable
-fun rememberScaffoldState(
- drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
-): ScaffoldState = remember {
- ScaffoldState(drawerState)
-}
-
-/**
* The possible positions for a [FloatingActionButton] attached to a [Scaffold].
*/
// TODO(b/200553810): Mark as experimental
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/MutableCollectionMutableStateDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/MutableCollectionMutableStateDetector.kt
new file mode 100644
index 0000000..896c7bf
--- /dev/null
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/MutableCollectionMutableStateDetector.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.compose.runtime.lint
+
+import androidx.compose.lint.Names
+import androidx.compose.lint.isInPackageName
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.kotlin.descriptors.containingPackage
+import org.jetbrains.kotlin.psi.KtExpression
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.types.typeUtil.supertypes
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService
+import java.util.EnumSet
+
+/**
+ * [Detector] that checks `mutableStateOf` calls to warn if the type is a mutable collection, as
+ * mutations to the mutable collection inside the state won't cause invalidations. Instead, it is
+ * recommended to use mutableStateListOf / mutableStateMapOf for observable mutable collections, or
+ * mutableStateOf with a read-only collection inside, and assigning a new instance when the data
+ * changes.
+ */
+class MutableCollectionMutableStateDetector : Detector(), SourceCodeScanner {
+ override fun getApplicableMethodNames(): List<String> = listOf(
+ Names.Runtime.MutableStateOf.shortName
+ )
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ if (!method.isInPackageName(Names.Runtime.PackageName)) return
+
+ val expression = node.sourcePsi as? KtExpression ?: return
+
+ // PsiType will return the underlying JVM type for kotlin collections, so instead we need to
+ // use the KotlinType to preserve the actual Kotlin type declared in source - that way we
+ // can disambiguate between MutableList and the underlying java.util.List that it will be
+ // converted to.
+ val service = expression.project.getService(KotlinUastResolveProviderService::class.java)
+ val bindingContext = service.getBindingContext(expression)
+ val expressionType = bindingContext.getType(expression)
+
+ // expressionType will be MutableState<Foo>, so unwrap the argument to get the type we care
+ // about. We do this instead of looking at the inner expression type, to account for cases
+ // such as mutableStateOf<List<Int>>(mutableListOf(1)) or
+ // val foo: MutableState<List<Int>> = mutableStateOf(mutableListOf(1)) - the inner
+ // expression type is mutable but because the type of the mutableStateOf expression is not,
+ // we don't want to report a warning.
+ val type = expressionType?.arguments?.firstOrNull()?.type ?: return
+
+ if (type.isMutableCollection()) {
+ context.report(
+ MutableCollectionMutableState,
+ node,
+ context.getNameLocation(node),
+ "Creating a MutableState object with a mutable collection type"
+ )
+ }
+ }
+
+ companion object {
+ val MutableCollectionMutableState = Issue.create(
+ "MutableCollectionMutableState",
+ "Creating a MutableState object with a mutable collection type",
+ "Writes to mutable collections inside a MutableState will not cause a " +
+ "recomposition - only writes to the MutableState itself will. In most cases you " +
+ "should either use a read-only collection (such as List or Map) and assign a new " +
+ "instance to the MutableState when your data changes, or you can use " +
+ "an snapshot-backed collection such as SnapshotStateList or SnapshotStateMap " +
+ "which will correctly cause a recomposition when their contents are modified.",
+ Category.CORRECTNESS, 3, Severity.WARNING,
+ Implementation(
+ MutableCollectionMutableStateDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+ }
+}
+
+/**
+ * Returns whether this type can be considered a mutable collection.
+ * Returns true if this is, or this is a subclass of:
+ *
+ * - [kotlin.collections.MutableCollection]
+ * - [kotlin.collections.MutableMap]
+ *
+ * If not, returns false if this is, or this is a subclass of:
+ *
+ * - [kotlin.collections.Collection]
+ * - [kotlin.collections.Map]
+ *
+ * If not, returns true if this is, or this is a subclass of:
+ * - [java.util.Collection]
+ * - [java.util.Map]
+ */
+private fun KotlinType.isMutableCollection(): Boolean {
+ // MutableCollection::class.qualifiedName == Collection::class.qualifiedName, so using hardcoded
+ // strings instead
+ val kotlinImmutableTypes = listOf(
+ "kotlin.collections.Collection",
+ "kotlin.collections.Map",
+ )
+
+ val kotlinMutableTypes = listOf(
+ "kotlin.collections.MutableCollection",
+ "kotlin.collections.MutableMap"
+ )
+
+ val javaMutableTypes = listOf(
+ "java.util.Collection",
+ "java.util.Map"
+ )
+
+ // Check `this`
+ if (kotlinMutableTypes.any { it == fqn }) return true
+ if (kotlinImmutableTypes.any { it == fqn }) return false
+ if (javaMutableTypes.any { it == fqn }) return true
+
+ // Check supertypes
+ val supertypes = supertypes()
+ if (supertypes.any { type -> kotlinMutableTypes.any { it == type.fqn } }) return true
+ if (supertypes.any { type -> kotlinImmutableTypes.any { it == type.fqn } }) return false
+ if (supertypes.any { type -> javaMutableTypes.any { it == type.fqn } }) return true
+
+ return false
+}
+
+private val KotlinType.fqn: String? get() {
+ val descriptor = constructor.declarationDescriptor ?: return null
+ val packageName = descriptor.containingPackage()?.asString() ?: return null
+ return packageName + "." + descriptor.name.asString()
+}
\ No newline at end of file
diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt
index 5473733..d59b1b8 100644
--- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt
+++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt
@@ -37,6 +37,7 @@
ComposableNamingDetector.ComposableNaming,
ComposableStateFlowValueDetector.StateFlowValueCalledInComposition,
CompositionLocalNamingDetector.CompositionLocalNaming,
+ MutableCollectionMutableStateDetector.MutableCollectionMutableState,
ProduceStateDetector.ProduceStateDoesNotAssignValue,
RememberDetector.RememberReturnType,
UnrememberedMutableStateDetector.UnrememberedMutableState
diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/MutableCollectionMutableStateDetectorTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/MutableCollectionMutableStateDetectorTest.kt
new file mode 100644
index 0000000..9d57ef0
--- /dev/null
+++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/MutableCollectionMutableStateDetectorTest.kt
@@ -0,0 +1,1586 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.compose.runtime.lint
+
+import androidx.compose.lint.test.Stubs
+import androidx.compose.lint.test.kotlinAndCompiledStub
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestMode
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/* ktlint-disable max-line-length */
+@RunWith(JUnit4::class)
+
+/**
+ * Test for [MutableCollectionMutableStateDetector].
+ */
+class MutableCollectionMutableStateDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = MutableCollectionMutableStateDetector()
+
+ override fun getIssues(): MutableList<Issue> =
+ mutableListOf(MutableCollectionMutableStateDetector.MutableCollectionMutableState)
+
+ /**
+ * Extensions / subclasses around Kotlin mutable collections, both in source and compiled form.
+ */
+ private val KotlinMutableCollectionExtensions = kotlinAndCompiledStub(
+ filename = "MutableCollectionExtensions.kt",
+ filepath = "stubs",
+ checksum = 0x90938fc8,
+ """
+ package stubs
+
+ fun mutableList(): MutableList<Int> = mutableListOf()
+ val MutableList: MutableList<Int> = mutableListOf()
+ object MutableListObject : MutableList<Int> by mutableListOf()
+ class MutableListSubclass : MutableList<Int> by mutableListOf()
+
+ fun mutableSet(): MutableSet<Int> = mutableSetOf()
+ val MutableSet: MutableSet<Int> = mutableSetOf()
+ object MutableSetObject : MutableSet<Int> by mutableSetOf()
+ class MutableSetSubclass : MutableSet<Int> by mutableSetOf()
+
+ fun mutableMap(): MutableMap<Int, Int> = mutableMapOf()
+ val MutableMap: MutableMap<Int, Int> = mutableMapOf()
+ object MutableMapObject : MutableMap<Int, Int> by mutableMapOf()
+ class MutableMapSubclass : MutableMap<Int, Int> by mutableMapOf()
+
+ fun mutableCollection(): MutableCollection<Int> = mutableListOf()
+ val MutableCollection: MutableCollection<Int> = mutableListOf()
+ object MutableCollectionObject : MutableCollection<Int> by mutableListOf()
+ class MutableCollectionSubclass : MutableCollection<Int> by mutableListOf()
+ """,
+"""
+ META-INF/main.kotlin_module:
+ H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM3Apc0lkZiXUpSfmVKhl5yfW5BfnKpX
+ VJpXkpmbKsQfnJdYUJyRXxJckliS6l3CpcbFWlxSmlQsJOtbWpKYlJPqnJ+T
+ k5pckpmf51pRkppXDGQUA9XxcbGUpBaXCLGFAEnvEiUGLQYASQTUpX0AAAA=
+ """,
+ """
+ stubs/MutableCollectionExtensionsKt.class:
+ H4sIAAAAAAAAAJ2Wa2/bVBjH/yc3O26aOEkva7q1LL2v7ZyNXSgNZaVsLCzp
+ YEWTUF85rRXcJg7KOanGu34WPgHQF0ggoYiXfAW+C+Kxa9nOWYI2pNjnnOfy
+ P7/H56L89c9vfwB4gFcMS1z0m9xo9IXZbFsH3XbbOhF213n6RlgOpw5/IRQw
+ Bv3MvDCNtum0jJfNMwpSEGeY6Fwn1m0uGArrG3UvrC/stuHadhnW3jJW66FW
+ zRFWy+rt7lHkUr3baxlnlmj2TNvhhuk4XWG6NNw47IrDfrtNUcVQbL/XM39w
+ FRWkGVJV27HFHkN8feN1BhPIaNAwyZAdnl5BjmwtSzSi7BNDI12uI4MCimnE
+ MMWg+UUfWRSZHyqPTIS4KtvGVTwbncU5t06fm/w7ilcwp6Hksk8O6Si4SaYQ
+ 3SPQooOcBJPBIj5wuW+H3A3ze5mbTISzI9tGcb9PKSShYFXDmlSKZ98YKsWD
+ 0qKDnMSXwRa23VLuEn1H3rAMN4boQw/BbY9xjVuXqVHBCj4kT0gcnTs/wjYz
+ miaDR3jslvERg1o9afubduUdD8nyu+2sx/97ITff60vl6+ddQTUYDUuYp6Yw
+ yRbrXMTpfmHuK8nAzt1OjOxvbLdXod7pPYa/B5er2uBSi+mL9KhaTI1Te9tv
+ V/x2idpUKaPnS2ohUYhVYhVGo0IwSpUKerGUvR75No0ipoKIbHlWH1yG6X/+
+ mFJp3lJCjesJ8iVDrdCl6Gr5lp6WhcOACT1DuZPhLKErp+tuifcZfQMs/MfV
+ evecTmz6yG45puj3LIb5V31H2B2r5lzY3Kas/fAKZEgcdE8t91zYjnXY7zSt
+ 3jeuMh2bo26/d2I9s93BnK/x+i0F3KNdl6AVSNDdQtcZjeruymAXDWpTRKtS
+ W3KvjMBXlXx0BgPfJ5KPNnbg2xvypTGHJI3iOKTRBvnp42Ayvvs7tG9/RXYA
+ /Sd3y+AlvTVyu5AZeq7FlCGxOeQDqZueEEX/gulQIeVZk2OyZzA7CqREIPMD
+ 3JJB0gSS9qVuSFILgVQEpCyDaGOyl7A8CmSNQNYHuCODZAkk60utSFKbgVQE
+ xJBBcmOyK7Q1rrO3fBA9ujQDPJBZiqRV9NXuS2oPA7UIy47MMjUyW8XHVCXz
+ soXP8iTKcoXp6GJdoRz9ZFcwJPAr7PwczFz25kjSDEni1zBPzwL17tCzTURV
+ emL4yuN6ga+pPaPMTyn6yTHiNezX8Bm9cVDD53hawzN8cQzG8Ry1Y0xzPOL4
+ 0vs9pMPGUfD6eY4Ux5bX3+RY5lj0+gscsxwVjiSnv2GY+RfzLRDjlwkAAA==
+ """,
+ """
+ stubs/MutableCollectionObject.class:
+ H4sIAAAAAAAAAKVWW1cTVxT+zoSQIRlhEg03r62oIWkJalU0FEWKJQpBAalC
+ WzuEMQ4kEzszoWpv9Gbvtxcf+thnH+pqi1rX6qL2rT+qq/vMDBMIg9LlWsm5
+ 7H32t69nn/nn3z/+BPASfmTYaVqVGTM9UrGUmaI6UC4W1byllfXRmTlahMAY
+ SsNzyoKSLip6Ie2QMw6lYmnFdFWkd9W5rG6pBdXI9GWG58tWUdPTcwultEZU
+ Q1eK6ZJizKuGmT63Tm+GQa5VF0IdwzY/lSHUM3T9Pw0hiAz1vZquWX0MgUTn
+ pIQwImEEITFsrarpNwzl5rBmkv7GMJo4t7HKdRhRBqmjY1YtqgXFUq90k/XD
+ a89kJGzFtgYIiDPUWdc0k2H38BOjTjEIKLOzdDyR7ZxiiK6LawjbGUILSrGi
+ jl5laKZzPsGXsBO7wtiB3QzxxPosdk5JaMNzEcTwPMGREyVVtxhYlgJEBvQX
+ iwxtCd9kZ7hhaX9eb8q3ErjE3uGyUUjPqdaMoWi6mVZ0vWwpXMhM58pWrlIs
+ kveia4opIsmwyy+/BGsQgJY3Q3iB3MtfU/PzLsJ5xVBKKh1kOODj9irKOAcp
+ ZHgJdCEdxovolrAfB3hIDlFc/V1nCOaLqmJIOIIIP3qUTM6XdYu7JKHHCelx
+ hsgK0Y5k1yai5ZrYZ+cm4xjyMuVGMwdL162bdr0S5yROcU4/6dXIT8UqG7wK
+ OlcpyLp0sjbly/C/rhIG8ArHPkM1YKil8oLKkX3O8nR2PCmdlAle3QQ55ETk
+ HF0vHyCGBkcRRUlCznF61Kby4NnUCw51jEJRUK1x7ZZqh4IKtc6kjYRJXOQH
+ XlvTPnKV0gy/LJd5nHRrkt8XCdO4GMYUXufF3x6mi/kmQ2x9DngibbbCHcjy
+ VV7CBBcWQOpDVtnuEAyJxLTP5fKhMezpnTjhk+7E9MQESdBAtaVwVBFzDPv8
+ Kr9aOROOASFQcfVtcE03aZmEAvQwSihTjnwtjyf8PUpt1B820GJyLdRkxN58
+ 0e7BPJzUWsVsbnyiPzcwKOEGWnm/pMjuq2mm/jVLHXLllRmhkplVLIVoQmkh
+ QK8c40OQmto8kW5ofEddWpg9yNix5cWusNAqhAV5N/3FsCAGaY7QHKB5V3h5
+ kSZii3U0d/JZ/Pu20Lq8eEiM1cWEbqGbnQ6JwuOf6wU5cDYqh9qFbvFQvdxA
+ szD0+A472yKHbZokR9pXZCSb0yBvIU6jvY7KTTWSAZKUN5SMco69bpJjHner
+ Ldcsb6u1wrYvTojNGyC2yC3+nLGYXMdRLj2+EySU+vY6MSjX8/BRdwS15nUv
+ 2OANS6W2TA2ga56yvH2soltaSc3qC5qp0cn+aoegGz6uFXTFqhh0neoGyrM0
+ NQ1ruurc2wmOzK9mOa8UJxVD43uX2FGL6/X8NQq2jFtKfn5Eue6KhcfLFSOv
+ ntH4ps3FmFxnGQ5S+QXpHwIvxDZekeTtF1Q79TR3AnKMv+q0/pJoAt6lncC/
+ IojyFVEGEKA9EE/ex5ZkoPcRmi7fh7yM2O9ovmeLfE1j2DmEKP2/oZVE4nzf
+ Qup4eKk/uYBH6SQ/25j8Dc3bH2DPQ+wVcNc+xSWbHa4ryVcd2Ec8xl8zMoZj
+ HHcxoqlY6gEOcqTUQxxeDxP1YKJI0mfiipcJfEtziDkhoPGI53DC1kUecdSH
+ OMZwzwN1vAp7XvU8yasTm/Mq8xSv+jbvVW+tV/S4+nt1mq0CrfWKnk3Xohqp
+ Vxl+2UDKMWBwdWHRSI8NUTnSYde3iOvV8HqvIp5XEQrOCPE8b87aR3NPidT5
+ Z4jUhadgjz8DNj20/lm4tGEW5H7+OetKnXItkpKpZUwt4Y0lXLnr3bx696ZV
+ DZM8wyQK5Fu2dz0e2piLFk/9hR0/IRi4W4Wd4bABfGePrGED/LiHH/fwhzz8
+ URc/tgZ/xxJmNwUe88BjHvikF8C0G8BgcglX/WPgxDHoVXMB19waPOZa1pSK
+ zfPMLiNEGb5eLWnHgibPgibM4W0vr5oNZriW7HctETnMA1RqL4booohY8FpL
+ i41P9f0IAjXRd37FrXs2IeCqCOB7e/4cP9Bskth75Mn70whk8UEWH9KIRT58
+ lMXH+GQazMSn+GwarSb/3bZ/EROTJiZMXDTRZlM6TOw3kbTXR+yxx0TGxEkT
+ A/Z2yETWRM7Ehf8A1Au5SlEPAAA=
+ """,
+ """
+ stubs/MutableCollectionSubclass.class:
+ H4sIAAAAAAAAAKVWW3PTVhD+ju+RBZYNzg3KpQRw7DZOoEDAaSCk0BicBJKQ
+ QtKWKo4wSmSZSnIK9Jbe6PWZhz72mYcy0wZoZzopj/1Rne6RFSWxZZIOM/Y5
+ 0u7Z7+y3u2eP/vn3j78AvIGfGPabVnXOzI5WLXlOU4YrmqYULbWiT1bnipps
+ mmEwhnJhQV6Ss5qsl7Ljcwu0IleTVC1Vy64bDWxYl9ctpaQYucFcYbFiaaqe
+ XVgqZ1WSGrqsZcuysagYZvZyw845Bql+uzACDLu9tgwjxNDz/3YII8IQGlB1
+ 1Rpk8Ke6p0UIiAoIQmTYtb7NkGHI9wqqSfvvFBDj2p3r2poiziB2dc0rmlKS
+ LeVmL3lf2LwmJ2IXdrfAhyRDwLqtmgwHC1vEnaLgl+fnySCV755hiDdENow9
+ DOElWasq47cYWmmdR/hFvIJ9AvZiP0My1ZjH7hkRHTgYRQKvEhzRKCu6xcDy
+ FCJyYEjTGDpSnunOccey3rqBjGctcItDhYpRyi4o1pwhq7qZlXW9YsncyMyO
+ VayxqqYR+4jjihlBmmGfV4YJ1iAAtUhV+hrRK95WiosOwhXZkMsKLWQ46kF7
+ g2SSg5RyvAh6kBXwOnpFHMFRHpJjFFdv6gzBoqbIhogTiPKlJ8nlYkW3OCUR
+ /bWQnmaIrgntSPZsI1qOi4N2bnI1R96k3KjmhfId655dsaQ5i3NcM0T7qsRT
+ tioGr4LuDRvkHTl5m/FUeB9YEcN4i2NfpBowlHJlSeHIHmt5OrtelE7KBK9v
+ ghypReQyHTAPIIaW2kYUJRFjNdLjtpQHz5ZerUknKBQlxZpU7yt2KKhQAya9
+ iJjGNb7gnU0NZKxanuOH5QaPk25N8/MiYhbXBMzgXV78nQIdzfcZEo054Im0
+ 1TInkOdPRRFT3NgH2j5sVewewZBKzXocLg8Zw4GBqTMe6U7NTk2RBQ1UWzJH
+ jWCB4bBX5a9XzlTNgTCouAabHNNteiaiBF1AGRXKkafnyZQ3o0yz/tBkF5Pv
+ Qk3mcF2n9C5Han5rV8goVcO8bMkk85WX/HSJMT4EqV8tkuiuyt+oBfvm+xjr
+ W13uEXztPsEn7ad/RPBFgjRHafbTvE9YXaaJ1JEAzd18bl9dPhZJBBK+Xl8v
+ Ox98/kvIJ/kvxaVwp683ciwktdDsG3n+kF1qkwRbJkrRzjUL0da0SDtIs9N+
+ jkuxOks/WUpNLeNcYz/HpISr3WXbtUq7672w/UsSYmsTxDapzVszkZACHOX6
+ 84ecZagzEAlKIR44anmgfttwMV24aynUa+lU9yxS6lom1ZIuW1WDTkFguDJP
+ U6yg6krtuE1xW36iKkVZm5YNlb87wq6Jqm6pZSWvL6mmSiK3VQ+tdw6GPfXL
+ Nml3TFpycXFUvuOACpOVqlFULqr8pcMxnW4wRB+d2yD9w2gBkxL8Yia2X1HV
+ +LBEvYPxDwEavybJMPwkBZLpJ9iR9g/8idiNJ5BWkfgdrY9tk29oFOxFrYjT
+ /wE9iWRORmhDO69P3mAcwJO0kq/dmf4NrXue4sAzHPLhkb3qgY1hax1L/tSF
+ w6Rj/DoiZzjGaQcjnklknqKPI2We4XgjTNyFiSNN33prLFP4luYwR+22TU64
+ hFP2XsSIoz7DKYbHLmiNleCy6n8RqzPbY5XbgtXg9lkN1LOi29Gb1Xm2AbSe
+ Fd17jkd1Vm8z/NrEqubABXxHc8jdn24LknKk4w63qMOq0Mgq6rKKUnBGSeey
+ uWQvHdsiUldeIlJXt8CefAlsuim9s3C9aRakIf496lidczwS05lVzKzgvRXc
+ fOSevJBt17rBMdF1TKRAfmCz63fRJhy0ZOZv7P0ZQf+jddg5DuvH9/bIWprg
+ J138pIs/4uKPO/iJTfh7VzC/LfCEC55wwafdAGadAAbTK7jlHYNaHINuNZdw
+ 26nBU45nsUxikWd2FWHK8J31kq55EHM9iGEBH7p5VW0ww/HkiONJhMM8RbX+
+ YEQcFL9j7ccP9vwlfqTZpDUfkZN3Z+HP414e92nEx3z4JI9P8dksmInPsTyL
+ dpP/vrB/URPTJqZMXDPRYUu6TBwxkbafT9hjv4mcibMmhu3XERN5E2Mmrv4H
+ PrD+CPEOAAA=
+ """,
+ """
+ stubs/MutableListObject.class:
+ H4sIAAAAAAAAAKVYi38TxxH+7mRLsnTGZ4FtbBPzMkG2ADkkJQl2IIZAIjAm
+ YNcluG16lg5zth5Ud3JJ2iQmbZK+36UtfSZN37SFJjE4tKlD+kzbf6m/zuyt
+ z5J8MvqRH+ZutTvzzTezs7Mjvf+/t98B8AD+q6DDdkpTdvJEyTGmsuaIZTsn
+ p2bMtBOCosAYmTHmjGTWyE8n3elBd6bkWNkkCw+VSaTyjjltFgcPDI7MFpys
+ lU/OzOWSFs0W80Y2mTOKs2bRTh4vszWoQK82EUKDgnWVZkIIKojXixpCWEFw
+ yMpbzgEFgXjfhIYIohE0QlOwfgV6uFg0nnE11kXQwqtab2/GzJrThmM+PUDs
+ qvwd1NCKWBNUrFfQ4Jy3bAWdIzViSN4FjEyGBOOpvrMKWlfFKoROBaE5I1sy
+ T55T0E5yPgHV0I1NEXThHgVt8dV70ndWQwc2RxHCFoIj+jkz7yhQUgoa46lU
+ 34SA9lGcYMUdrHgviVr5jHmRIkech7NZcixe5v3hQjZLOlYhP8i+JP3XhhK+
+ CcEa20cKxenkjOlMFQ0rbyeNfL7gGKxkJ0cLzmgpm6WAhSV7O4zdCnr8tpxg
+ iwRgpe0QkhSR9HkzPSsRnjSKRs4kQQU7fSJVNjPGINMiBPdhbwQDuF9DH/o5
+ GB+iePm7rqBrOZB+URmosVgzLGzyITa5n+KfzppGUcMQojzzCAUjXcg7HCwN
+ B939fVRBdHlS7NGeOvZBOu/aO+S6+Bjl5rTpcEJV5JyU1XAUj7PcE5RQIi84
+ PzmPUzVyMKXhGEZY5QSr2EdyF5xnxOkjoyfxJK+cIpcs2hzDKRQ5JfvKuKfk
+ PIU44bvgX2o0jGGcsSnHo1nDdlIuWQ1nXDZP0ZnO0pFMeXa7KuBHytYGOa9r
+ LdayP4mPsp2PK+j2QumHPFB7tTb0Jxh6is5k0cwV5kwOmo8sp17vWseLTgaX
+ JoLMuHk0TXXQB0hBk2uIckvDjJsqs7RrctZhAJEXeSHKeShEL7iin6QNtzmr
+ Orjs+BrY6VuI/DLQRokx5yid7NIUx0vBhhXclZqsoM9nulZQL+IZhv00OXCu
+ WMil3KIXcgrLIzoWY9azpshdyvYGmz5ouIR5Vnux4sYaLeWmuIp/jhM770xw
+ IdfwMuYjeAmvcHHdGKG74gsKYn4edqCXl7/Mx1sIfpXP3SYefZ2P03EefZNT
+ WYy+zdE3eXRZQ86V+x7fC6/wghD5AYfN4dGPNLzAPFT8RHgn7jq6ROOTPuH3
+ mVOwZWh8v08ViU+Oj5MGPahkGYwaxuu0M35lKIRfKNjhV8RXZMZdaiH8SsGB
+ GjdOnZw1vIrfRPBr/JbS29entri/r4laV10NK39gK9do14fSWdFncKCpfQin
+ RsfGh0cPH9HwJtq5T3iLAlBXZlJ/sNw3naCDlTEcg+bU3FyAejWFH410pdNh
+ VC9a/Im6EzVzn6Kmlub3RdSNakTVt9L/cEQNN9I7GlmapxdNhwP07qE3Lel9
+ 9GbRBL0j4fdeVjcuze8Nxxpi6oA6oBwKhdXbrwVVPXCsVQ91qQPhvUG9id7q
+ E7cvK8c6xVyE5qI8V7G2WdekvLum6c1dy7jrhESHlFi90qS3MK4Yt+p6ld0A
+ abbW0GzXY5JJdFla8F9POBtWcWzS2xhHjFv0dg+rQ1hp1Teu0mjROz2pLulF
+ 2UyZXebSXR0xwWUT6dxTk39PFX9F8u/QN9fQ6dG3lOuUR0robqW92ObpKLS+
+ Xcr1Lts4HdMbeHzm9uVG0gl2NYQb9SAn1F6Fkg09spddOQlHLjom9Vx0m+yZ
+ pTrcfbqUd6ycmcrPWbZFksMr1w0V1jFrOm84pSIV0YbDhQy9WkasvOmWy3FG
+ 5opYSBvZCaNo8Wc52VuN6zV0FQaaxxwjPXvCuCDVImOFUjFtHrX4Q6fEmFjF
+ jDo9lb4EqFTH+Wh28hklb/9KpylI712AHuP+nsZ/ozkVN9BLT/ruQDN/p5nD
+ CNBnoK3/Bpr7A0O30PLUDehLCL2FDdeFyj/oGRFCQYIK4p800kidlOhfO59l
+ vhYk4D6SZNl1/W9iQ/dN9Cxiq4qrQoo1291VqcmjbdhOa4zRKzGGiRQXhBaB
+ sUmA7AzgugeyxV32QFoQFyA8YjiVxtT5khTDPSwptSZie27iAQZNLGLfalat
+ HmArduNBL2gJ/IveIcWNqMB+SGI/JqnGdnnY3bsWMRgoA3fZxjzwmMc2Jsyo
+ 0szDwkyTKhRdQ0PeRsVFjGgn2MQi6GvgSjjc3Yh4u3Fwrd0Yrm83Dt0hfEfq
+ D9/h6vBRR0DTq/ktIqViCV3X1uDnhq4swelJfUVtf0fr85caev9In1bK9Ksj
+ Ta26jFKV1kcUXKuh5Qblw1U+nFnLh7P1+UCNuz+bp+/E5mNVbKhPl0j3SzZR
+ uUNpFdXbE/WoROX2uKBGFSi1enLbK0Apmc6v9q8cdBssWvOS6JwQnblDgmY/
+ QILmaiZo4a4S9MIduBY/AFfqjiVXrxaVlc1PBSr5rlWL3MpZyZy+W0jmByS6
+ 5qIv4jMBVONqHq6G5ySuhucFrkv/2Sp86uj9j91nax47fZh/G5Jaj8p4av2J
+ Jby0gM8v4ItXsXxtBYVesCys5Qy34UtQBNoOiXZc+tjc371rGe4r133hXIeb
+ PbhmL5DNAljFKf6JQwKfljTbEu+i6woaA1dX+H6N+QbwvngqTTWIt3mW2jzi
+ R/G4xN8v8YP93Qv4xrU1AhD0cIKS8Sn+maMGT7WM57fujifhn6kL/zt3jZ/x
+ 4nxS4scq4ty1gO/WBV5+LlbAlwvzIxK8iYP8/QVcWSvRmjyoJjfOtF8Zb78q
+ oH5IJ3WtLfODslFaI2d/7A9XT85e8g5kUh7Ixv4F/NTfVfdcNnrX4at4Tdai
+ B6WDLYnYz7nOLeGXiZu4Wl04y1u41/E7r8r9TID9XjK5VzIJM8xNXK++y8IS
+ JYw/ev1Sh8Cn2+MWVOpo33gDC9fFRECaCODf4v0e/kPvK6R2kzxZnEQghbdT
+ uEVP/Ikff07hHfxlEopNpfTdSeyw0W7jtviL2rhk4wUb8zY6xMw28Yzb6LOx
+ W4yHbBy0ccjGURubbByzcdLGmFg6Y2NSDDI2TBszNnI2LtiwqajbuGjjORvP
+ /x8chhqA3RgAAA==
+ """,
+ """
+ stubs/MutableListSubclass.class:
+ H4sIAAAAAAAAAKVYi1sU1xX/zSzsc5BhFRAw+MK4sOoSk5pGiAaNJquoUSg1
+ 0jYddkcc2AfdmaWaNgm2TdMmfT9sa59J07dttU1QkjYl9vn1j+rXc+5cht1l
+ FvczHzBz995zfud3Hvfcy/73f++8B+AR/EdBl+2Up+zUqbJjTOXMUct2xspT
+ mZxh2yEoCozRGWPeSOWMwnTqzNSMmXGG3JmyY+VSLD5cIZEuOOa0WRo6NDQ6
+ W3RyViE1M59PWTRbKhi5VN4ozZolO3WywtqQAr3WRAhNCjZUmwkhqCDRKGoI
+ YQXBYatgOYcUBBL9ExqiiEXRDE3BxlXokVLJuOJqbIiilVe1vr6smTOnDcd8
+ bpDY1fg7pKEN8QhUbFTQ5FyybAU9o3WjSP4FjGyWRBPp/gsK2tZEK4QuBaF5
+ I1c2z1xU0EFyPiHV0IMtUXTjAQXtibVZ6b+goRNbYwhhG8GRA3mz4ChQ0gqa
+ E+l0/4SA9lGcYMVdrPggiVqFrHmZYkecR3I5qo9Ehf9Hi7kc6VjFwhD7kvJf
+ G076lgRr7BwtlqZTM6YzVTKsgp0yCoWiY7CSnTpddE6XczkKWFiyt8PYq6DX
+ L+kEWyIAK0NlmqKIZC6ZmVmJ8IxRMvImCSrY7ROpipkxBpkWIXgI+6MYxMMa
+ +jHAwfgQxcvfdQXdK4H0i8pgncW6YWGTH2aTByn+mZxplDQMI8Yzj1MwMsWC
+ w8HScNjN7xMKYiuTIkf7GsiDdN61d8R18UmqzWnT4YKqqjkpq+E4nmK5p6mg
+ RF1wfXIdp+vUYFrDCYyyyilWsY/l55wrYv+R0TN4hlfOkksWJcdwiiUuyf4K
+ 7mk5TyFO+i74NxsNYxhnbKrxGG06J+2S1XDeZfMs7eocbcq0Z7e7Cn60Ym2I
+ 67reYj37k/gY2/kEtQIvlH7Ig/VX60N/kqGnaE+WzHxx3uSg+chy6fWtt71o
+ Z3BzIsisW0fT1Al9gBREXENUWxpm3FKZpazJWYcBRF0UhCjXoRCdc0U/RQm3
+ uao6ue34Gtjt24j8KtBGmTHnqZzs8hTHS8GmVdzVrqyg32e6XlAv4wrDfoYc
+ uFgq5tNu0ws5xZURbYsx63lT1C5Ve5NNHzRcxQKrfa7qzDpdzk9xF/8CF3bB
+ meBGruGLWIjiZbzCzXVzlE6LLyuI+3nYiT5e/gpvbyH4Nd53W3j0Dd5OJ3n0
+ LS5lMfoOR9/k0TUNeVfu+3wuvMILQuSHHDaHRz/W8BLzUPFT4Z047egYTUz6
+ hN9nTsG24fGDPl0kMTk+Thr0oJZlMGoYb1Jm/NpQCL9UsMuvia/KjLvUQvi1
+ gkN1TpwGOWt4Hb+N4jf4HZW3r0/tCX9fk/WOujpW/shWbpJvDRUdHf0rl6JT
+ tGeyhmPQnJqfD9BVTOFHM53WtM/UyxZ/oquHmn1IUY8uLxyIqpvVqKpvp79w
+ VA030zsWXV6gF02HA/TupTct6f30ZtEkvaOblxf2h+NNcXVQHVSONN99I6jq
+ gRNteqhbHQzvD+oReqtP372mnOgSc1Gai/Fc1dpWXZPy7pqmt3SvoG4QEp1S
+ Yu1KRG9lXDFu0/UauwHSbKuj2aHHJZPYirTgv5FwNq3hGNHbGUeMW/UOD6tT
+ WGnTN6/RaNW7PKlu6UXFTIVd5tJTGzHBZQvpPFCXf28Nf0Xy79S31tHp1bdV
+ 6lRGSuhup1zs8HQUWt8p5fpWbJyL6008Pn/3Guc72N0UbtaDXEr7FSoz9Mor
+ 6mp5H7vsmHSRoiNi3yw118iYNV0wnHKJel/T0WKWXq2jVsF0u9w463IjK2aM
+ 3IRRsviznOw7Vy44Vt5MF+Yt26Ip7x42snoM0QFZK1a12jLmGJnZU8acBI2O
+ FculjHnc4g9dUnVijSJd31S626vUnCNQ9Dhf0cnbv9E+UvEn7KQxXf/puUwz
+ RxGgWaB94DZaBgLD76L12dvQlxF6G5tuCZX36RkVQiGCCuEujTRSJyX66eAd
+ y31dAh4gSZbdMPAWNvXcQe8Stqu4IaRYs8NdlZo82iEoMUafxBghUrztWwXG
+ FgGyO4BbHsg2d9kDaUVCgPCI4VQa09WVpBjuMUmpLRnfdwePMGhyCQfWsmrz
+ ANuwF496QUvi7+w9k9wjVOiOKrGflFTjezzsnj1LGApUgLts4x543GMbF2ZU
+ aeYxYSaiCkXX0LCXqISIEWWCTSyB/pNbDYebjaiXjcPrZWOksWwcuUf4jjUe
+ vqO14aMjnabX8ltCmqe7b67Dzw3dP2gc9PDoYlDf39ON+Us3cv9In1Mq9Gsj
+ TXdtGaUarY8quFlHyw3KR2p8OL+eDxca84Fu3v5snrsXm4/XsKGLtkR6WLKJ
+ yQxlVNSmJ+ZRicn0uKBGDSjd1WTaq0CpmC6t9a8SdAcsWvOK6KIQnblHgeY+
+ QIHm6xZo8b4KdO4eXEsfgCtdbyVXrxdVtM1PB6r5rteL3M5ZzZz+OZDMD0l0
+ zUVfwmcDqMXVPFwNL0hcDS8KXJf+8zX4dCX333afr7vt9BH+ckdqPSHjqQ0k
+ l/HyIr60iFdvYOXYCgq9UEVYKxnuwGt0PjLaLol2UvrYMtCzZwXuq7d84VyH
+ Wzy4Fi+QLQJYxVn+jkICn5M025Pvo/s6mgM3Vvl+nfkG8E/xVCJ1iLd7lto9
+ 4sfxlMQ/KPGDAz2L+ObNdQIQ9HCCkvFZ/p6iDk+1gue3748n4Z9vCP+7942f
+ 9eJ8RuLHq+LcvYjvNQReuS9WwVca8+MSPMJB/sEirq9XaBEPKuLGmfKV9fJV
+ BfUj2qnrpcwPykZ5nZr9iT9cIzV71duQKbkhmwcW8TN/V9192ewdh6/jDdmL
+ HpUOtibjv+A+t4xfJe/gRm3jrLzCvYnfe13u5wLsD5LJg5JJmGHu4FbtWRaW
+ KAGpHcC/xPs9/Jve10nmz0TyrUkE0ng7jUV64jY/7qSxhHcmodh4F3+ZxC4b
+ HTb+Kn5jNq7aeMnGgo1OMbNDPBM2+m3sFeNhG4dtHLFx3MYWGydsnLExJpbO
+ 25gUg6wN08aMjbyNORs29Wsbl228YOPF/wOZ1FsqfRgAAA==
+ """,
+ """
+ stubs/MutableMapObject.class:
+ H4sIAAAAAAAAAJ1X63cT1xH/3dXT6wXW8hPxMAkmtqUkMpQkEBs3xnWMbGP8
+ ICbBeXQtL2atlUR0Vw7kSfNOkzbP0jYf+5kPcNqSkJyT49Jv/aN6MrO7liV5
+ cRyO0d65c2d+M3fuzNzL//7/w08AjuNbgQ7pVJZk5mzFMZZs86xx5dzSqplz
+ YhAC5alVY83I2EZxJeOxBz1OxbHsDMkO1Qhki465YpYHA1jDg1P5kmNbxczq
+ WiFjEbdcNOxMwSjnzbLMTG4aHxTQG23GEBbYVWc3hqhA7w4xY4gLRIesouUM
+ C4T6+hc0qGhWEYEm0LkJPGUV8+byGUNedrV2q9jDElpPz7JpmyuGY74yILCn
+ PgSDGlqQaIKCVoGwc9mSAl1TwUGl3UVytmmUNXSiuRkxdAk050pFx7CKctK8
+ Rgh92f6LAi1bghjDfoHYmmFXzHOX6NhILiDSGg6iW8UBHBJo79t6fP0XNSTx
+ MJs+TLHIs0mRpfBuOLHABjT0ejJ95LBrkmRXTEegZ6pUXsmsms5SmaUzRrFY
+ cgzHKhE9XbFt3i7t8pEg01tZGtJ4jO08Tluz5FjhinPNPSHycgBHeeUYza9U
+ yHJnXzZwxwInAozt0PxxPMlGnqL8ICMjti2Q6Gs43/4FgaEG5lA6wJMg3rCr
+ fni7qJUcDhztI3ypXCrEcUrgYFBmE2SZlK2cjOG3dLq5y2Yu72vPGGWjYJIg
+ VcX20ZhnkJVBroERnFbxDEY1nMTTHIYxCkPZLJTWKAHGvYM5I6DSwY+xbZMy
+ u6WvvyYS8yan9Ewjb6g+WD2sfW3HrWKYIOMmqxCUhrOYZEemKUPIEaoRyekV
+ bLEBibZDCe6izHko8x7KvPW66eYZZX5Y0kTDBSywwPMCTSTgVgEXcp2h0ZJt
+ Uzjp2Aj6sfss3dcVt46khpewyJZerut005XCEtf472nvdOJ+FS5hQYWBHNfs
+ PpVaDHmdCMrjXm/5cvDxBzeKNDcKBXmmVpkqcD08wVSJz99dfZUpd5U8n8Ak
+ UxUNMx71mobn2EUF1zRcxCJTb9AOhnK22215Tg00np2ePz8yPTqm4R10cKu8
+ LvDUA94klIMbl8lZ0zGWDccgnlJYC9GNJvgToZaWJ9ZVi2fUsZXlo0J8tX79
+ hKp0KaqiH6FfXFXiTB+mXy/RNNe7aQzRqKjr12lopqlKYw9N4//9UOlav35s
+ dyKcUAYU7zsgTsfiyr1/RBU9NNGkJ5LKQOuZezfERIveRnT7sajeQaPi8zp9
+ XleV16XvTYa7xIBSIxly8ZKEt4/lXblD+v56uVoUV/7ARLd+kO0fa9O7k3oi
+ Ghc1fh7yrT20xZqnnZzr0cPJtoSeqN9fdCDy/L0bEZKJJsPxmB6f69KbknF/
+ tXZNpbWE3syoNVxN30Uau6sae2rWdL2FT4b6O6gozY0GE867FX7Qvzo362rs
+ qmNS96OW+XieroN9c5WiYxXMbHHNkhZJjmz2VKrheWulaDiVMhVMeLS0bPK1
+ bRVNr8zOMzJXUiln2AtG2eK5z+xpxK221joDu+YdI5envPXV1PlSpZwzn7V4
+ stfHWNjiGY5S8kfoFwOXwV6uB9r+l5SnURqHAD3BzwmivyKegndJRuHXCnG+
+ Js4oQjQH2lPfYVcqNPQj9rzwHfR1xP6Nttuuyjf0VV0hnaB0/IUojdRJif46
+ uEr49eED9tHIa2rqX2i7i70Ct12JTS21qkVNyNd6kvDZwm7W2vc9HrqLHgU3
+ q5od3qqvydQRPOJa6t0Oo397jJSPQZ2L1hjjpI/RsomRUbCOA7caYFqqMC2+
+ Kxsxf9SVpNdGcEB+I2pcagwItUvfkXE6l5B7Lr4j+11nToTqnTnkCVWdafed
+ YYp3pzS4RRczadTtNJ0Y/h6/YzPpu3hWqTmurTs9VZNJg7hBY0x4aUbf8e2i
+ mH2gKNIF4bvbEMVzAptY9VH0vJuqrQH6ztwP6fwvIc02INEFFXyyL2x3snSZ
+ Bdt/5Zfsv1hnf5Zf2779OT/M7en/4MC3iIRuptLrMO5g+Q4usSsh/NX9iia3
+ iKMuul4T//rEWaH1WX6p7wDf+tX4KR+fHgf3wRe3NvHtWw/kvz7C/wPw8Z/2
+ 8aOp9B0Ub2GjmW3FiVZxolUcesr7OLN+LbZuuPfohpdXgkG9qmytgrb6wWWK
+ w8BJNb6jMJQfOAzjwWFwflUYyE96LvtVnfETN5K6g7VgGC9/Iw2VOMuv5QCQ
+ qzsHmaW/C9XCqwV5/eYOQGb5kRzgwZs79+BFxPFW9ZbrdOMFNP8Ihe7Lt/+J
+ P9x2GSFfPIS/ueMX+DuNX5LaewT4PjWBLD7I4kP64iP+fJzFJ/jjIoTEp/hs
+ EQ9LdEj8yf3XLPHnDXpCYlLic4kZiQsSz0ksSLzkLl2UWJTodOmk+z0i0SuR
+ kkhLdEsMSByXeELipMQpV2D8Z8BtlfOyEQAA
+ """,
+ """
+ stubs/MutableMapSubclass.class:
+ H4sIAAAAAAAAAJ1WWXcTRxb+qrW63UBL3oRYTIKJbYlEhpAEYuMJMMTINuAl
+ MYvJ0pYb01ZLIqqWA1mZ7MlkliQzeZjHeeaBnDNDQnLOHA+P+VE5ube7LUty
+ 23E4sqtuV9373Vt3q/r5lx//B+AY/imQkk5tQebO1xxjwTbPGzdnawsF25Ay
+ BiFQnVw2VoycbZSXchcXls2CM+yt1BzLzhH3SANDvuyYS2Z1OGBpdHiyWHFs
+ q5xbXinlLFqtlg07VzKqRbMqcxPr6ocF9FadMYQFdjTpjSEq0L9NzBjiAtER
+ q2w5owKhgcE5DSraVUSgCfSsA09a5aK5eM6QN1ypnSp2MYfW17do2uaS4Ziv
+ DQnsanbBsIYEkm1Q0CEQdm5YUiA9uZlb6XyRgm0aVQ09aG9HDCmB9kKl7BhW
+ WU6YtwljID94VSCxwY0x7BWIrRh2zbx4XaCb+AJ8rWE/elXswwGBroGNARy8
+ qiGNx1n1QfJGkVWKPDl4zYg5VqCh3+MZIINdlcS7ZDoCfZOV6lJu2XQWqsyd
+ M8rlimM4VoXoCzXb5gPTKZ8IUr1xSUMWT7Kep+holjxbuuncdmNEVg7hCO8c
+ pe+bNdLcM5APPLHA8QBl21R/DM+ykucoQ0jJKdsWSA60RHhwTmCkZXEkG2BJ
+ 0NqoK35wK69VHHYcnSN8vVopxXFSYH9QbhNklYStAlXnHyi6hRtmoehLTxlV
+ o2QSI9XF1t6YZZClYa6CUzit4gWc0XACz7MbzpIbqmapskIJMOYF5pyASoE/
+ y7pNyu3EwGCDJ2ZNh+yeal0baXZWH0vf3nazGCXIuMkiBKXhPCbYkAuUIWQI
+ 1Yjk9ArW2IJEx6EEd1FmPJRZD2XWest084wyPyzpQ8MlzDHDZYE2YnCrgBSl
+ mhSdqdg2uZPCRtBPbrK1qSluHUkNr2CeNb3a1Osu1EoLXOOv09kp4n4VLmBO
+ hYEC1+welZoMWZ0MyuN+b/tGcPiDG0WWG4WCIlPLTJW4Hp5hqsLxd3ffYMrd
+ JcvHMcFUTcOUR72p4WU2UcFtDVcxz9TbAs894jVB6bV2U5w3HWPRcAxaU0or
+ IbqwBA8R6lZFWrpl8Re1Y2XxiBBfrt45riopRVX0Q/QfV5U40wfpv59o+tZ7
+ aQ7RrKird2hqp0+V5j76TK3eObozGU4qQ4o3DonTkYf/jip6aLxNT6aVoY5z
+ D78V4wm9k+iuo1G9m2bFX+vx11L1tZS+Ox1OiSGlgTPk4qUJbw/zu3wH9L3N
+ fI0oLv++8V59P+s/2qn3pvVkNC4arDzga3tsgzZPOj3Tp4fTnUk92Xy66FDk
+ 8sNv+YTRdDge0+MzKb0tHfd3G/dU2kvq7YzasKrpO0hiZ11iV8Oeric4JtS0
+ QZVmrnWNcNEt2/3+jbheLGdvOSa1NOqDTxWpx7fNWktlw6lVKc/DZyqLJt+3
+ Vtn0quMlluUCqBQMe86oWvztL+6ZqZUdq2TmyyuWtGjp1HqLpVurdbfeL5vY
+ dsw6RqFIGeuDqrOVWrVgvmjxx24fY24DPo5Q2kfoP4Y2CD3JLwI6/peUoQre
+ RYpoenDQ+BdaOYMQrQJdme+xIxMa+Qm7rnwPfRWx/6LzO1fkrzSqLlPC/f2N
+ KI3ESYh+3VwL/HzwAQdo5j018x90PsBuge9cjnUptS5FXcSXepbwWcNOltrz
+ Ax57gD4Fd+uS3d6uL8nUITzhaurfCmNwa4yMj0Gth/YY44SPkVjHyClYxb57
+ LTCJOkzCN+XvREdpPuxy0nMh2CFPiwaTWh1C/c43ZIziEnLj4huy1zXmeKjZ
+ mAMeU92YLt8Ypvh0SotZdLOSRNNJs8nRH/BHVpN9gBeVhnBtPOnJhkwaxlc0
+ x/hwI67I2FZezD+SF6nD++a2ePGiwDpWsxc96ybrSJ5xU5shvfRbSNMtSHTD
+ BEf2ylaRpdsoWP9rv6X/WpP+aX4u+/pnfDd3Zf+Pff9CJHQ3k12FcR+L93Gd
+ TQnha3cUbW4RR130RIP/mxNnifan+am9DXzrd+NnfHy63TfBF/fW8e17j2S/
+ foqf8D7+8z5+NJO9j/I9rDWzjTjROk60jkNvcR9n2q/FjjXzDq9ZeTMY1KvK
+ jjpoh+9cptgNnFRj23JD9ZHdMBbsBud3uYHspPeuX9U5P3EjmftYCYbx8jfS
+ UonT/NwNALm1fZBp+l2qF14jyFt3twEyza/cAAve2b4F1ygAHhXCN+78Z/yD
+ 5q+I9z3ifZ/qO487efyJRnzAw4d5fISP5yEkPsGn83hcolviM/evXeLzNXpc
+ YkLiC4kpiUsSL0vMSbzibl2VmJfocem0Ox6S6JfISGQleiWGJI5JPCNxQuKk
+ yzD2KyjpAQxSEQAA
+ """,
+ """
+ stubs/MutableSetObject.class:
+ H4sIAAAAAAAAAKVXW1cTVxT+ziQhQzKYSbhjS62iBtISBItFkIoUNBpASUpV
+ 2tohjDAkmdiZCVV7ozd7v7340Mc++1BXW8S6Vhe1b/1RXd1nMg4QBmUtF+Sc
+ mX35zv723uec5N///vwLwFH8xNBkWuU5MzlRtpS5gppRram5JTVnBcEYrqSX
+ lGUlWVD0hWRFPFiRlC2tkCTboU0GKd1SF1RjcHgwnS9ZBU1PLi0XkxpJDV0p
+ JIuKkVcNM3luY6VBBrl6gSD8DHVbFgmihuHwLjGDEBlqhjRds4YZfPHOGQkh
+ hEMIQGJo3gBOa3penT+jmIu2154QItxC6uiYVwvqgmKpV3oYIlv5DkqIIlYL
+ AfUMfmtRMxla0t4ZJHY+ZX6e7OKpzssM0W2pCqKVIbisFMrq1FWqBNl55FPC
+ XjwTQhueZWiMb69I52UJzXgujCD2ERwFX1R1i4GlKBEUwEihwNAa30RktFQo
+ kKdW0gd5YElv3VDCs7jc40C6ZCwkl1RrzlA03Uwqul6yFO5kJidL1mS5UCD2
+ ohOKKSLO0O5VQII1CEDLmUF0Eb3coprLOwjnFUMpqmRIxfegvUmS4SALg7zU
+ L+DFEBLoltCBgzwlVMMmb+oMgVxBVQwJfQhz06MUcq6kW5yShP5KSo8xhB8J
+ 7Ux27yJbTojDdm0GKoHQekHNHCtes27YfUmaExjmmldoXY14KlbJ4F3QuWmB
+ lCMn74SnwnsHShjBKY79KvWAoRZLyypH9rDl5ex4XDmpErytCXK8khFqq3oP
+ IIbaykKUJQnpCukJW8qTZ0unKtLzlIoF1cpoN1U7FYToN+lFQhYZbvDaloNh
+ slyc45vldZ4n3Zrh+0XCJWRCuAi7+VtCtCPfYIhtrwEvpK2+wgmc5k+KhGnu
+ LCBHgVilEcNQqCbx+KzH5vKQMewbyh73KHd8NpslDxqotxSOKmKRocGrX4JY
+ YjjotSc2bLKV0IKgthveYQPvMmYJKvQQiihR9Tw5Nca9uSZ2Ojl2WMXkq9Dx
+ Iw7lCvYpzBNNB6uYmsxkRyZHxyRcRxM/Qm/wztvFdUJH56MbZYJ6aV6xFJIJ
+ xWUf3WGMDwE67fIkuq7xN9rywvwRxo6tr3SHhBYhJMgH6COGBDFAc5hmH83t
+ ofUVmkgt+mnu5LP4zy2hZX2lV4z5Y0KP0MNOBUXh4S81guw7G5WDbUKP2Fsj
+ 19IsnHl4m51tlkO2TJLDbY98JFtTK9eRZo/9HJUjVZ4+8pR39Ixyjf0ckWOu
+ tt72a5IbqqOw42skxKYdEJvlZm/NdEz2c5SLD28HCKWmzS8G5Bqevl5GqUW7
+ c6dtlH3suqXSeU0nQ3eeirx3uqxbWlFN6cuaqZHlyMbRQVs/oy3oilU2aJv7
+ R0vzKr9PNV2tbOgsR+Z7tpRTCjOKofF3R9hRjeteBlsWqMtYSi4/oVxz3EKZ
+ UtnIqeMaf2l1MGa2RYYj1H0B+gTB+7CVNySx/ZJ6p4bmw4Ac4/c8PX9FMgHv
+ oZ5G+hpBkq9JMgofvQONXfdQ1+UbeoDIpXuQ1xH8Aw13bZdvaAzZRmGCCuNb
+ epLInZzor4l3Lj+4HMB+suS2e7p+R8PeNbTfx/MC7thW3LOponU8+dN+HCAd
+ 49ccBcMxBhyMaCLWuYYkR0rcx5HtMFEXJoo4el2Wh/AdzUFWSQGNfS7huL0W
+ MeKo9/ESw10XtMIq5LLqfxyrl3fHauAJrIZ2z+p4NSu6db1ZnWSbQKtZ0X3q
+ RFTlNcbw6w5elQBGNzcWjXQLkZQj9Tncwg6rs9tZhV1WYUrOOdK5bM7Ypukn
+ ZGryKTI19QTsC0+BTTewdxVmdqyCPMK/5zpeJ52IpK7EOi6uYnYVb95xd16N
+ 7RfeFJjkBiZRIt8i/QX+Fc9Bm3bQGhN/o+1nBHx3NmDf5rA+fG+PrHYH/EYX
+ v9HFH3fxpxz82Bb8tlXM7Qo85oLHXPCsm8Ckk8BA1yrmvXNQyWPA7WYVV50e
+ POZEFknENF7ZdeQTa7i20dKVCCJuBBEs4h23rgs2mOFEcsiJROQwayhXbwzR
+ QRGx7B4tzTY+BfoAAh2i7/6Gm3dtgc9Zwocf7PkL/EizSW7vE5MPZuFL4cMU
+ PqIRK3z4OIVP8OksmInP8PksWkz6cYtb9n/YRNbEtImMiWZbst9Eh4m4/dxn
+ j/0mBkycMDFiv46bOG0ibWLqfxuJKgUvDwAA
+ """,
+ """
+ stubs/MutableSetSubclass.class:
+ H4sIAAAAAAAAAKVXW1cTVxT+Tu5MBjOJ3MTWWkUNpCVcLBahVKSi0QBKUqrS
+ 1g7JGIZMJnZmQtXe6P367EMf++xDXatFbNfqoj72R3V1n8k4QJgoa7kg58zs
+ y3f2t/c+5yT//vfn3wBO4meGLtOqLZnpmZolL2lKTrFytaWCJptmGIzhRnZF
+ XpXTmqyX0nNLK0rBGqtLapaqpcl6fJtBRreUkmKMTYxly1VLU/X0ymolrZLU
+ 0GUtXZGNsmKY6Utba40xSI0LhBFgaN2xSBghhhN7xAwjwhAaV3XVmmDwJ3sX
+ RAiICghCZOjcAs6qelkpXpDNZdtrn4AYtxB7eoqKppRkS7kxwBDbyXdMRByJ
+ FviwnyFgLasmQ3e2WQ6Jn18uFskymem9zhDflawwDjCEV2WtpszdZOggO4+M
+ ijiIFwR040WG9uTumvReF9GJl6II4zDBUfgVRbcYWIZSQQFMahrDgeQ2KlNV
+ TSNPtaqP8cDS3rrxlGd5ucfRbNUopVcUa8mQVd1My7petWTuZKZnq9ZsTdOI
+ fcQJxYwgyXDIq4QEaxCAWqCO6yN6hWWlUHYQLsuGXFHIkMrvQXubJMdBSmO8
+ 2K/gVQEp9IvowTGeEqpihzd1hmBBU2RDxDCi3PQkhVyo6hanJGKkntJTDNEn
+ QjuT/XvIlhPihF2b0XogtF5YNc9Vbll37M4kzRuY4Jo3aV2VeMpW1eBd0Ltt
+ gYwjJ++Up8J7D4qYxFmO/Rb1gKFUqqsKR/aw5eXseVo5qRK8sQlyup4Raqv9
+ HkAMLfWFKEsisnXSM7aUJ8+WztWllykVJdoo6l3FTgUhBkx6EZFHjhu8veNo
+ mK1VlvhmeYfnSbcW+H4RcQ05AVdhN3+XQHvyXYbE7hrwQtrqG5zAef4ki5jn
+ zj4UKBCrOmkYMtUkmVz02FweMobD4/nTHuVOLubz5EED9ZbMUSNYZmjz6pcw
+ VhiOee2JLZt8PbQwqO0mmmzgPcYsQoEuoIIqVc+TU3vSm2uq2cnRZBWTr2Lx
+ ptrDXUGn4pPrYobapChbMsl8lVU/XVGMD0E6yMokuq3yN9rNvuIgY4Oba/2C
+ r8sn+KSj9IkIvkiQ5ijNfpoPCZtrNJE6EqC5l89dm2tDkUQg4RvwDbCzwce/
+ hnyS/2JcCnf7BiJDIamFZt+Fx/fYxU5JsGWiFO1+4iHamhaplTT77Oe4FGvw
+ 9JOn1NQzzjX2c0xKuNr9tl+H1NYYhR1fOyF2NEHslDq9NfMJKcBRrj6+x1mG
+ ugORoBTiiRtilFQccq6qrVqeu20pdAjTdu8vU+VacmpJl62aQbszMFUtKvwi
+ VHWlvg/z3JdvtWpB1hZkQ+XvjrBnvqZbakXJ6KuqqZLIPcMnt44UhoONZju0
+ rTlLLpRn5FsOqJCr1oyCMq3ylwOO68IuRwzShg7SJ4wWMCnBr2pi+xV1jQ+r
+ 9jN9E6Dxa5JMwU9SoL3vIVr7/ON/IXbtIaRNhP9A2wPb5RsaBduI3/oivrWf
+ GHeivw7en/zkcQBHyJLb7uv7HW0HN3DoEV724b5txT076lrHkz8dwVHSMX5P
+ UTAcY9TBiKcSvRtIc6TUIwzuhom7MHEkMeSyPI7vaA5z1BO2y7BLOGmvRYw4
+ 6iO8xvDABa2zElxWI09j9freWI0+g9X43lmdbmRF16Y3qzNsG2gjK7oQnYga
+ vM4x/NbEqx7AFL6nOeSuT9cISTnSsMMt6rC6uJtV1GUVpeRcIp3L5oJtmn1G
+ pmafI1Nzz8C+8hzYdIV6V2GhaRWkSf5F1fE640Qk9qU2cXUdi+t4776780K2
+ n7gtMNENTKREvk/6K/w7moM276C1p/5B9y8I+u9vwX7AYf34wR5ZSxP8dhe/
+ 3cWfdvHnHPzEDvzudSztCTzhgidc8LybwLSTwGDfOoreOajnMeh2s4KbTg+e
+ ciKLpRIqr+wmyqkN3Npq6XoEMTeCGJbxoVvXkg1mOJEcdyKJcJgN1Bo3RsRB
+ 8Tvefvxoz1/iJ5pNsvmIgry9CH8GdzK4SyM+5sMnGXyKzxbBTHyOtUX65YkO
+ E1/Y/1ETeRPzJnImOm3JERM9JpL287A9jpgYNfGGiUn7ddrEeRNZE3P/Axpj
+ kXLPDgAA
+ """
+ )
+
+ /**
+ * Extensions / subclasses around Kotlin immutable collections, both in source and compiled
+ * form.
+ */
+ private val KotlinImmutableCollectionExtensions = kotlinAndCompiledStub(
+ filename = "ImmutableCollectionExtensions.kt",
+ filepath = "stubs",
+ checksum = 0xf50711c2,
+ """
+ package stubs
+
+ fun list(): List<Int> = listOf()
+ val List: List<Int> = listOf()
+ object ListObject : List<Int> by listOf()
+ class ListSubclass : List<Int> by listOf()
+
+ fun set(): Set<Int> = setOf()
+ val Set: Set<Int> = setOf()
+ object SetObject : Set<Int> by setOf()
+ class SetSubclass : Set<Int> by setOf()
+
+ fun map(): Map<Int, Int> = mapOf()
+ val Map: Map<Int, Int> = mapOf()
+ object MapObject : Map<Int, Int> by mapOf()
+ class MapSubclass : Map<Int, Int> by mapOf()
+
+ fun collection(): Collection<Int> = listOf()
+ val Collection: Collection<Int> = listOf()
+ object CollectionObject : Collection<Int> by listOf()
+ class CollectionSubclass : Collection<Int> by listOf()
+ """,
+"""
+ META-INF/main.kotlin_module:
+ H4sIAAAAAAAAAC2KMQrCQBBFRzQWUyhsZSdYiYFcQiwkZbzAJhlwYXcnZP5C
+ ju8WFv+9V3wi2hPRrq75m7jli8/zqmHeuknToibdWjJCEncesl/sqxjgIT34
+ zo2hjOau75QK/BjlqTHKhKD5tUGy1bD6PPEBYnDHT2WPGz3oB8hyC49/AAAA
+ """,
+ """
+ stubs/CollectionObject.class:
+ H4sIAAAAAAAAAK1W21MTVxj/nQ1JlmWVEI0IXhoraiDFIFpvpFSkWINcVJCq
+ tLVLssLCZjfd3VDs1d7svX3zodOnPvtQp61inWmpfesf1el3dtdNhMVxHGfg
+ nLPfOd/v+323c/Lvf3/8CeAQfmDYYjvVGTs3aOq6WnQ00xifmadFHIxhbmRe
+ WVRyumLM5jxxnyepOppep5KvO1cwHHVWtfr6+0YWTEfXjNz8YjmnkdQyFD1X
+ VqwF1bJzZ0aVSkUtjbqffQyJ1ZbiaGDYHGYtjhhD5xODxyEyxPKaoTn9DJFM
+ 55QMCU0SopAZ0j5OMYCvj4V9hnhsZGhUyxXn2ohmOwzJTGddELisT0YCLRKa
+ kWSQOzpKqq7OKo56pYf8Wnt2M1KNELCFocGZ02yGrSPhOaCwiEXTcBTNoFMN
+ mULnZYaWNaGOYztDfFHRq+r4VUoonQvJh4ydeE7CDqQZNj7KKY7nGVKZtcnu
+ vCyjHR1N2I09ZILcKqsGhYAVGJoeMhvQdYa2TGhh9HHG+8P38tm1Bvtdhd0j
+ pjWbm1edGYsbyCmGYTqKl5sx0xmr6joPjU/HFtHNsDOsHsh3iwC0oh1Hjlws
+ zqnFBR/hrGIpZZUOMuwLcb1OMsFBZvt43RxAr4QeHJTRiS4elhcp3uGeU7w0
+ e4iXjVt0FMmjOMZVjhN1jewqjmnxbNVXU8GXk3Y2dCO802Tk8RLHfpmszqrO
+ hPau6lqlPDXY9CFjECf5gVdIrJRK5HMN54JhVysV03LU0niFmyH6Q0tFteI1
+ 26sMBwN5WrPTlIx0oJG+alppS1VK3aahX0vX2khEYVVR1cVRwhkJpzFCjUls
+ 3ArKPUGV1DzmZRIt6qpCIYxZatlcJIeTa9NIrevtujZEb12gLtlVb+5q1XCN
+ 5c5aakkrUuu6Bo48/ky+e11qQoVaf+fjLbjUeAe51Oruv7FqeYZ39WVeKIYz
+ xRtbxus4KWEab/CO3CbR/XFFxgCXCVBknPJkRcq/Yw5YlkJVl8lMh7R0iIxu
+ wfzk8ZBuzExPTpIGDRRthaOKmGPYE9ZrtZRNegTimGfoX+deeEJmMlToEhZQ
+ ZtgUyjyVCfcou96FtI6Vt7kVKiYxX9Tdp4KHk94HsTA2MTkwNjgkYxFt/N5+
+ hwKw6lIP70q6qx8+gaOU6JLiKCQTyosRensZH6J0lS6QaEnjX1QyQukAw08r
+ 17dLwlZBEhI76V+UBDFKc5M/Z8R/bghbV673ismGpNAj9LCTcVF48HNMSESG
+ WxLxdqFH7I0lGmkWTj+4GRluTUiuTE40tQc6tMOGGxMy33HXzYkNwe5Grnc+
+ mWjgGBcf3IwSeqy9QYwmYpxjLyP+SBfK5aqjzOhqLbxDS45K9y1d1PsX6J3Y
+ dr5qOFpZLRiLmq3RyYHaTU7lP6HNGopTtah1GwbNEk3NI5qhevU/yZF5T5tF
+ RZ9SLI1/+8KO1bjBZf6IgQ0TjlJcoN8Cvpo0YVatonpK4x9tPsbUGmZ0ywv0
+ 40BAHDzfbTzx5O8NSlGM5heARJI/4rT+gmQCrmETjfSbgiRfkiRPXwLNG7vu
+ YkNXJL+MTb+j9bZ79isaJXc3RXMKX9NKJj06ja1kh0eWGtxHOhwg/YbWbcvY
+ dQ97BdxyT3HNLd6ur8lX+5ChPcbfJ0RcjGM+Rks2uX8ZhzhS9h4Or4VpCWBa
+ 0I0jgXtZfENznHm+00gPmc/Ps0UecdR7oPKugXpeSYFX9ET5jFZpnWD4ZR0t
+ j0B/fexppGsv3P7Q4+yfCqLaSzy4w9J9nL6UHL6L0b9WhUIKQiEFER2jiApP
+ oV8fynFanQ3qJEM4bB2c1ezPoeMp2Z932U88A/ZZWk3iwlPhvIaLAc4UrS49
+ Ez7n+M9TPy7n/TpPZf/Gjh8Rjdzqyq5g+g7evIO3eFlE8K07ska3C2NuhFN1
+ dlKBnZQft3P8d5OPn/NrLdp1BzO3gmauh/GSFvVgEgNUcg/JnfDJyTVOpXAM
+ j4ocUJGDFKq46ofsiI/WnE1qvKVXEM8uw6h1kQfSHIA0Yw5mELdZF6ziU9vr
+ +yVymGXYq3tR9FFEOEHltrr4QNN9CJfuovorlm67gohvIoLv3PlzfE8zT/27
+ FJf3phEp4P0CPqARH/LhowKu4+NpMBuf4NNpNNtos/GZ+9dkY9DGgI2TNtpd
+ yT4bnTa6bRy1kSfJ/3e4UsLJDgAA
+ """,
+ """
+ stubs/CollectionSubclass.class:
+ H4sIAAAAAAAAAK1WW1MURxT+evbKMMqyCgIqWSPqwgYX0XhjQwSCcZGLAhKV
+ JGbYHZaB2ZnNzCxBczOJuf4AH/KYZx+SqkQxqUqIj/lRqZyeGWfXZbAsK1XQ
+ 3XO6z3e+c+vef/79/U8Ap/ADQ4dlV5es7JihaUrBVg19rrpU0GTLioExrEyu
+ yutyVpP1UnZmaZVODLmSqq1qdUq5unN53VZKijk0PDS5ZtiaqmdX18tZlaSm
+ LmvZsmyuKaaVvTwlVypKccr5HGJINFqKIcywN8haDFGG3hcGjyHOEM2pumoP
+ M4TSvQsSRDSLiEBiSHk4BR++PhrWZeKxm6FJKVfs25OqZTMk0711QeCyIQkJ
+ tIpoQZJB6ukpKppSkm3l1gD5tf3sXrQ1QUA7Q9heUS2GrsmdskCBiRcM3ZZV
+ nc6F0/nemwyt24IdwwGG2LqsVZWZZYZ2OheQEQndeEXEQaQYdj/LKoZXGdrS
+ 29Pde1NCF3qacRhHyAQ5VlZ0CgLLMzQ/ZTaiaQyd6cDSGOKMjwfv5TLbDQ47
+ CocnDbOUXVXsJZMbyMq6btiym51pw56uahoPjUfHiqOfoTuoIsh3kwDUAhV0
+ llwsrCiFNQ/himzKZYUOMhwLcL1OMsdBSkO8ck5gUMQATkroRR8Py+sU72DP
+ KV6qNc4Lxyk7iuRZnOMq54m6SnZl2zB5turrKe/JSTsTuBHcaxJyeINjv0lW
+ S4o9p95RHKuUp7BFHxLGMMoPvEViuVgkn2s413SrWqkYpq0UZyrcDNEf3ygo
+ Fbfd3mY46ctTqpWiZKR8jdSyYaZMRS72G7p2O1VrpDjyDUVVF0cRl0VcwiS1
+ JrFxKij7AlVS85iXSaSgKTKFMGoqZWOdHE5uTyM1r7vr2Ii76zx1yaF6c8tV
+ 3TGWvWIqRbVAzesYOPP8M7n+HakJFWr+7udbcKjxDnKo1d2A09XyEu/qm7xQ
+ dHuBN7aEdzEqYhHv8Y7cL9INckvCCJcJkCVcdGUFyr9tjJimTFWXTi8GtHSA
+ jO7B3Pz5gG5ML87PkwYNFG2Zo8axwnAkqNdqKZt3CcSwyjC8w73wgswkKNBE
+ rKHMsCeQeVs62KPMThfSDlY+5FaomI403NjBDUfX8NP3bYpyWJRtmWRCeT1E
+ TyvjQ4RuyTUSbaj8i6pBKJ5guL9194AodAiikOim/7goxCM0N3tzumPr7mA8
+ GU4KA8IAG408+SkqJEITrYlYlzAQH4wmmmgWLj25H5rYlxAdmZRo7vI1aIdN
+ NCUkvuOsWxK7/N3dXG82mQhzjOtP7nP0aFc4HklEObtBRsyRypfLVVte0pRa
+ zMY3bIUuUbp9j6/R5d80p5Z02a6a1HHhMaNIU8ukqitu2c5zXd6KRkHWFmRT
+ 5d+esGe2qttqWcnr66qlksi/g0dqNzzD/sZjz+zumrPlwho98R6oOGdUzYJy
+ UeUfnZ7qwjZFuroFevMFxNAElkjyd5j8/YKSI8BGktb0s4DGL0mSI5lA8+6+
+ R9jVF8ptYs9v2PeLc/YrGkVnt53mdtyjlUR6dBod6OT55x3qIZ32kX7Fvv2b
+ OPQYRwU8cE7dczCEOk2+OoY07TH+wCDkYJzzMFozyeObOMWRMo9xejtMqw/T
+ in6c8d3L4GuaYxz1NUeFXiKPn2uLPOKoj0FFXAN1vRJ9r+iN8Rg1aF1g+HkH
+ LZfAML6hOerbp3sr2P748+xf9KM6SDy4w+IfuHQjOfEIU381hEL0QyH6EZ2m
+ iAovoV8fyhlaXfHrJE04bAecRvZX0fOS7Gcd9nP/A/sMreZx7aVw3sF1H2eB
+ Vjf+Fz5X+e9LLy6zXp23Zf7GwR8RCT3oy2xh8SHef4gPeFmE8K0zsianC6NO
+ hNvr7LT5dtq8uF3lP3w8/KxXa5G+h1h64DdzPYybtIgLkxihkntK7oJHTqpx
+ KgZjuFQkn4rkp1DBsheyMx5aSyap8pbeQiyzCb3WRS5Iiw/SghUYftxKDljF
+ o3bU8yvOYTZhNfZi3EMJedohfOfMd/E9zTyrVXJ5fRGhPD7KY4NG3ObDnTw+
+ xieLYBY+xWeLaLHQaeFz56/ZwpiFEQujFrocyTELvRb6LZy1kCPJf/behL1p
+ DgAA
+ """,
+ """
+ stubs/ImmutableCollectionExtensionsKt.class:
+ H4sIAAAAAAAAAJ2V3VLbRhiG37X8IyvCFrZJYkMS4wCBJCAgadIUSprSJHVj
+ SCZ0Mp3hSAaNR2DLjHfNJGdcS6+gLSedHnSYHvYWei+dfrsoklHsTpIBaXe/
+ n/d79kfrv//9408A9/GKYZaLfpPb9U6nL5xm293sttvunvC6/tO3wvU5dfgL
+ kQFjsA6cY8duO37Lftk8oKAMNIZk2+OCoTC/0FD+vvDadoNsawy3PjCuNyKR
+ ui/clttb26DIm41ur2UfuKLZczyf247vd4UjMbi93RXb/XaboqqHXdH2fHsv
+ hOR2BKw4swxZt3Mk3slqJi4hbcCAyZBpuaKhUJPnjRXnNZGHlUUC4wwadylk
+ /AL/jivnNBe3jZpSeQgshSvKywy6oiSDiasoGbiCMkOaIHdkZU2987HqJqZw
+ TRJep4iOcxQn3HKOqPCjuG0Y4SdAk4KCvvkemgwmZjFtYAZz59BbEkZT73wM
+ yMQCbkvoOwxGpMtw9QJntI+EsTjCNWqtS8OCM1hmGCO4zYGixuDg8vD6Ju7h
+ viT+gma8vkfr4YkNmt78whv6YD7yRM983Cl5+NlbdeeTVmi8EezsliucfUc4
+ ZEt0jjW6B5h8pRjYoewkyP7Wkz1avcT+CsM/ZydzxtmJkbCu06MbCV2jthq0
+ M0FbozZdMa3xil5IFhLLiWVGo0I4SlcKVrGSOx8FNoMiSmFErnbFOjuJ0v/6
+ Oa1T3UpS16wk+VKRVuTKWHrtmpWNC0cBlyyTcseiKpErb1lyiquM1gDV/70C
+ lw7pc8zueC3fEf2eyzD5uu8Lr+PW/WOPe5T1JLqx6IrZ7O678lPwfHe732m6
+ vR+lMh2/nW6/t+c+8+SgHGi8+UABK3T6krQHSVTkrUSjH+Te4BFeUJsmXp3a
+ irwPQt9XMR99dqFvLeajAx761i/4sigjRSMNDRrVyMqkV1v7HWO/yNOCLXob
+ lCfpUvScq2QuqJSRCzWmlAJF/4ZCpJBW1tSI7CJKwwgqcYIsKWQDjYmYxmSo
+ MUBwI05gjMiuYnoYwa04QY4IcoFGLaYxH2oMENyNE+RHZC9iKcieDQiyahfO
+ sBKHKFJKMZCxYzKrocwAxIM4RGloto6H+JL6MvunAGJGQZyioDbkFDfUspzi
+ 7nu4Uzz4NVSvKZ2U+tOJdowei6Y8QQ/97tEyl6jqtqpdx0tqDyjza4re2IVW
+ x+M6vqE3ntTxLTbr+A5Pd8E4nuH5LiY47nF8r/5XOZY48qqf40hzLKj+PMc0
+ x5TqT3KUOBY5UhxVjuJ/gOL8xQkJAAA=
+ """,
+ """
+ stubs/ListObject.class:
+ H4sIAAAAAAAAAK1Y+18U1xX/3ln24TDIggICUbdG48JGFzXVBiiVGNMMIiaC
+ NEo0HXZHHJmd3c7MUkwfsa/0/W5smr7Td2tbbRKF2CbG/tY/qp+ee2d2dtmd
+ VcrHD3LvnXPv+Z7nPffIf/777nsAnsAHDEnHLc872UnDcU/NX9ZzbhyMYX7y
+ srakZU3NWsh65BGPUnYNUxwerTmhWq6+oNsjYyOTi0XXNKzs5aVC1iCqbWlm
+ tqDZi7rtZE+c1EolPX9SfI6Q6HoZcbQwbF4rJ44Yw8C6YeNIMMRGDctwxxgi
+ 6YFZBTJaZUShMKR8nFzRNEmeUbSc7LHq+gRJ28ywSS+U3CtcOENneqDO8BEF
+ SXTIaEcng7J7d1439QXN1V8aIosaz25F1yZI6GZocS8ZDiFO1nucXJHIFS1X
+ Myzab0mrA+cYOhrcG8cjDPElzSzrpy4ydNO5kBgo2IGdMrYjxdCVbgzjwDkF
+ fdjVijgeJThSvqBbZChTGVorWoybJkNvusaaqpdGuHb7w/dGM40CxwTDo5NF
+ eyF7WXfnbS4gq1lW0dW8CEwV3amyaXI3+Oo4CWQYdoRFney0CcDIOXHsIxNz
+ l/Tcoo/wnGZrBZ0OMuwNMb2GMs1BFkZ4dmQxJGM/DijYizR3yyHybbjllFAL
+ usv9usb1PryCwzjCAT7CEDWsvL5M/hUzDxcPq9okJKqCEXyUs45xFuc4zz+R
+ vRSsoxjnO0+RdwwyTXOLNg9+bVqqPp0UzIRuhF9WBU/jOMf+OIXe1BxX9ZRV
+ oHraTFCCm5SmaiC3r+E61MjONt1sJn8SJ7mcUwz9gUvDkIea7zaHfp5DT5ND
+ nfK8d5m3ptUGHEIfCCE3gz2DWQ77AlWJi3axoPphdouVFSXItPGyLqJH8W5x
+ 6EPBBZznbC8RWcvnKT2r6Gcsp1wqFW1Xz58qcZso044v5/QSX8Qxz3AooKcM
+ J0X3JhVwpC4W7ZSta/l9Rcu8kqrWtQTydclWk/IyLsrIYYHylJs+SxWTtBKX
+ vi+tNr/1Q00211z7qrc4S3YdhWItRzRn6hqlWszWC8UlnRfMhhtD3vd2hc6y
+ rZdMLed97KkVeLFsCXHkZM2+4rmREopbPPzgc+E5wJmlEhX7XQ8WJPTkFVWo
+ FnF48eipZtsaZJ4rFNOGulsoaVWts+F7o/uaqtodDrbm9Z0qF+b5+/J5XmMs
+ d5Y/MQqu4ryMV/AF/l70y/SGfYlXuJ189RVesEb56qu8WIjV1xW8yDkkfFOB
+ 5nF8WzxTIRVvlh8x+JHv8VoddiSswC7jCuf5obhx47atUZVMp+dCuENo9PyP
+ zgyHPFDpuZkZ4qCBsk/jqAm8TtUiLHHjeINyLOxhqp6Z8VSL42cMY00e0XXq
+ rOA1/ELGz/FLhi2hNnWlw23NNHu9m0j5DZfyW0qA0ZwpuifuaGqZEurU9Mz4
+ 1LHjCv6IXt7K/IkcsK5qSU1MpR88Sbcgr7ka0aTCUoSaT8aHKPUdi0RaNvgX
+ 3Skpf4Cxo3evDsnSNkmWkin6TchSIkpzK80RmnfQHKM5TTM/M8jpiX+/Km27
+ e/VgorOlUxqShthT8YR0782YlIxMdCTjfdJQ4mAsuYlm6dl71yITPUlZ0JRk
+ a1+FR6EdNtGdbOOn6PTmymmB0044SX+ngsMmNiU7OI5Ytyc7A6wtQkpHcmsD
+ R3uyKzjVLSg9tZQauWxiZ7In2GG0s81H662cON2ZbOHrF+5di5KWsb6WRDQZ
+ 4348yMjHSKmFQtnV5k29mgLHl12dGijqvPYvUrnpP122XKOgq9aS4Rh0crza
+ mlH9mjYWLM0t21SIW44V8zS1TxqW7pWMGY7MK3Qxp5mzmm3wb5+4ux436M7W
+ CGibdrXcIrXwPps8XSzbOf0Zg3/0+hizDZrhAGVilH7j4DnZy5OT7F2hNIrR
+ PAgkO3nvTetVokn4C3bTSP8VIMq7RBmlL4nmzYO30TYYGV3BlnfQc1OcvUOj
+ LHZjNMfwT1opxEensY3kcM9STfSRDgdIb6OnfwUfWsVuCdfFKc7Z7e36nHy1
+ B4/RHuMNJyIC40kfoyPT+fgKDnKkzCqeaITpCGA6kMGHA/MG8C+a48yznUaq
+ 1ERu1G8VT0q4i+037qPfsNCvxpc0Ur1vbu/H1mcvtbI+Rlp8k5c5xiqOsRp+
+ z9Ny4GlqUn0v1XE9y3CjCZfnlGfqbFDvZ8OJ9dlALWu4Ns89SJupOm2oQ/WR
+ DvnatPoRmpFQH57WQJVWPzwe6Ok6UOpPfdAxmnlVVQToI6s4G6lBTXl7AaqC
+ cwKVr+ZoJfn4n6jDpyc+PISfvF8IteCyHCS53B75DnJnO/XbuPR+naVyoJMc
+ OJ16heDaelaF8Xs21fIP+zZ5SBKtL2PRvxYbReK3ruIdUyCmfcT/z7ba21ug
+ lRWUJl4UWBOces8WsWuDni0Jz37qIWg/IPQY3ZAewq/JceI/snF++rHhbMiO
+ MpYCO1xaffoh+YM61YeUsS/jMxu07HOBRp+lUtMXZMppv9x0ZT7A9jcQjVwf
+ zNzFK7fwxVv4Mr/EEbwnRrZJPIUxoVGsRk5XIKfLyySK4OEggsM+fmyw/xZe
+ vYHKk9qIEwtwYr7lz/O/hDTRU6rR82sb05Pw1XXhf2PD+BeCCpn1K2R08Ba+
+ dT3UDd51jnow5EQtCNJRXzmlqtN3wjE8VWpLeSUkGr7ro53wk7BtsP/xCtz3
+ b4bCeTnZFsC1BTnZJoAlAl7GD+4D/KPwkD8QmNav4Zqf7Ed8+9sznT/hHdFd
+ /DSzgl/Vv4vtAVw7Xsevg4z/sQB709fyMT8SCQ6zgt/VP9QJHyWB3wdVuEfg
+ 03t7B9LZ2/jDW/jzTUGI+CIieF/Mt6mdgrju1ymSf51DRMXfVPydRtzgw00V
+ /8Bbc2AO3sY7c+hz0OvglvjX6uCCgxcdnHc4nSh7HOx1kBHrww6GHex0MOLg
+ qIOnBVF1MCkWZxycczD3Pw/jQRHOFwAA
+ """,
+ """
+ stubs/ListSubclass.class:
+ H4sIAAAAAAAAAK1Y+XcT1xX+3shaPB5jWWAb2wFUliBbARmTQmO7Dg4hzRgD
+ CTZuggPpWBqbsaWROjNyTbqE7vve0DTd072lLbQJ2ElP6/Jj/6ie3vdmNJKl
+ Ebg+HGDe0333fne/7x3+89/3/gngSfyDIWE75Xk7M2XYznR5PpvXbDsKxjA/
+ taStaJm8Zi5mLswv6Vln1KWUHSMv2MdqOFTT0Rd1a3R8dGq56OQNM7O0UsgY
+ RLVMLZ8paNaybtmZs+e0UknPnRM/Rxni9TqiaGHYsVlPFBGGgS3DRhFjiIwZ
+ puGMM4RSA7MKZLTJCENhSHo42WI+T/qMomlnTlf3Z0nbDoZWvVByrnPlFKDU
+ QJ3jowri6JTRgQSDcvBgTs/ri5qjvzpEHjXy7kJXKyR0M7Q41wybYddUY8wp
+ GLFs0XQ0wySOlpQ6cJmhsyHAUTzGEF3R8mX9wgJDN/EFZEHBXuyTsQdJhq5U
+ YyIHLivow/42RHGA4Mj8gm6Sq0xlaKtYMZHPM/SmavypxmmUW3c0+Gws3ahw
+ XAgcmCpai5kl3Zm3uIKMZppFR3NzcL7onC/n8zwMnjl2DGmGvUF5Jz8tAjCy
+ VKpHyMXsNT277CG8oFlaQSdGhsMBrtdQpjnI4iivjwyGZBzFMQWHkeJhOU6x
+ DfacSmpRd3hcN4Xeg1dwAic5wIcYwoaZ01cpvmLl6eJpVZukRFUwig9z0XEu
+ Yp/hFSjql5J1ChP85BmKjkGuaU7R4smvLUzVo5OB6cCD4HZV8CzOcOyPUOqp
+ EB3VNVaB6lozSSWep0JVfb19DQ1RozvT9LCZ/imc43ouMPT7IQ1CHmp+2hz6
+ RQ49TQG1y/NuO+9KqQ04hD4QQG4GewmzHPYlmhMLVrGgeml2ipUdFci08Zou
+ skf5brHph4KruMLFXiWylstReVbRL5l2uVQqWo6eu1DiPlGlnVnN6iW+iWKe
+ 4bhPTxp2kvom6UskF4pW0tK13JGimb+erE62GHJ1xVZT8jIWZGSxSHXKXZ+l
+ mUlWiabvS6nNu36oyeGmtq9Gi4tktjAoNkuEs3ldo1KLWHqhuEKBTDR2DEXf
+ PRU2y5ZeymtZ98ehWoULZVOooyBr1nU3jFRQ3OORh/MF1wAXlko07vc/XJGw
+ k09UYVrI5sOjp1ptm5B5rVBOG+ZuoaRVrc4En40daWpqdzDYpvv3fLkwz++X
+ z/AZYzqz/IpRcANXZLyOz/L7ol+mW+zzfMLt47sv8oE1xndf5sNC7L6q4BUu
+ IeHrCjRX4pvimgqYeLOcxeAs3+GzOoglaMCu4jqX+b7ouAnL0mhKplJzAdIB
+ NHoAjM2MBFxQqbmZGZKgD1WfxlFjeJOmRVDhRvEW1VjQxVTlmXFNi+InDONN
+ LtEt2qzgDfxMxk/xc4adgT51pYJ9TTe7vZto+RXX8mvybUuDkN4nlcfeOSrw
+ nOZoRJMKKyF6WzL+CdOTYplIqwb/Re0i5Y4xNrJxY0iWdkuyFE/Sv5gsxcK0
+ ttEaonUvrRFaU7RynkFO371xYziWaElIQ9IQeyZ8/+2IFA9NdsajfdJQbDgS
+ b6VVev7+zdBkT1wWNCXe1leRUOiETXbH2zkXce+ocAucDsKJeycVHDbZGu/k
+ OGLfEU/4WDuFls74rgaJjniXz9UtKD21lBq9bHJfvMc/YXSy20PrrXBcTMRb
+ +P6l+ze5t5G+llg4HuERHGYUXSTVQqHsaPN5vZrXM6uOTq8iek4dXaYZ0jpt
+ LJqaU7ZofracLuZo6ZgyTN3t9BkuywdrMavlZzXL4L894sGLZdMxCrpqrhi2
+ QST/UTVRfbLRfV3Ptum0fdrRssv0MvdA5eli2crqzxn8R68nOtsgiGPU3GH6
+ F0UrWDzBn8/k79+pgCT8FgdoT695+r5DlDGiSbTuGLyH9sHQ2Bp2voueO4L3
+ XfrK4jRKaxR3aaeQHHFjN3p5jfKh5iGd8JHeQU//Gj6wjoMSbgkuLtntnnqS
+ fHcIj9MZ4y9GhATGUx5GZzrxxBqGOVJ6HU82wnT6MJ1I44O+ewO4x83lqINC
+ hEYtkRvtW8dTEjaw5/YD7BsR9q3RPuLj0cBu7u/TW/OX3qIeRkr8pihzjHWc
+ ZjXybqRlP9L0yvSiVCf1PMPtJlJuUJ6r80F9kA9nt+YDvTmDrXnhYdacr7OG
+ npge0nHPmjYvQzMS6tPT5pvS5qXHBb1YB0oPTA90nFY+OxUB+tg6Xg7VoCbd
+ Mx9VwWWByndztJM8/I/W4dMdHZzCjz0ohZrfLMOkl/sjv4/sywn9Hq79q85T
+ 2bdJ9oNOl73ftq5XQfKuT7XyI55PLpJE+yUse22xXSTedZXo5AViykP8/3yr
+ 7d4C7Ux/NPGhwJrg1Ee2iP3bjGxJRPbjj8D6AWHH2LbsEHGNT5D8ye3L0x8L
+ 9rb8KGPF98Oh3SceUTzoqfmIKvY1fHKbnn3at+hTNGr6/Eq56I2brvS/sect
+ hEO3BtMbeP0uPncXX+BNHMK6+LJWcRVGhEXRGj1dvp4ut5Iogyf8DI54+JHB
+ /rv40m1UrtRGnIiPE/E8f5H/V0YTO6UaO7+yPTsJX90S/te2jX/Vn5AZb0KG
+ B+/iG7cCw+C2c9iFoSBqfpJOecYpVZu+FYzhmlI7yisp0fBtD+2sV4Ttg/1P
+ VOC+eycQzq3Jdh+u3a/JdgEsEfAqvvcA4B8Ep/yhwLR/Aze9Yj/p+d+RTvyI
+ v4g28OP0Gn5Rfy92+HAdeBO/9Cv+hwLsbc/Kx71MxDjMGn5Tf1HHPJSQJx3C
+ e2L9G96nlXfy7yhJv59DSMUfVPyRvvgT/9xS8Wf8ZQ7Mxl9xew59Nnpt3BF/
+ 22xctfGKjSs2pxPlkI3DNtJif8LGiI19NkZtnLLxrCCqNqbE5pKNyzbm/gd3
+ 15BHbhcAAA==
+ """,
+ """
+ stubs/MapObject.class:
+ H4sIAAAAAAAAALVY+3/T1hX/XvmJowTFIU9GyWgAJ4E60NJC7bEmWQIm4RFC
+ QwkrVHGUVMSWPEnOSPcoe7R7vzfWde/uvbEtrG2Ass/G2G/7o/bZuZIsO46c
+ OIF9PrZ0de+533Pu95x77pH+898P/gHgGfydYbtpFWfM5Gm5cHbmmpK1ImAM
+ 1vg1eVFO5mRtPul0p5yeoqXmuGy6QiCjWcq8YqR8uo6nxhd0K6dqyWuL+aRK
+ vYYm55J52VhQDDM5RkgFZfa0/ZhikKq1RhBkaFylOYIwQ2/dqBFEGcJpVVOt
+ 4wyBRO+UiBgaYghBZOh0cbJ6LkfqVF2zmTDHSHMTQ1TJF6wl6mBoTvSuZiAl
+ QkJzDNsRZxB7emaVnDIvW8rVAeJ0jegOtG6DgDaGoPWqahLeeBXvtPyGrK5Z
+ sqqZY8oSCSYyvdMkuIbVCD7EEFmUc0Xl7BxDG8n5UC/iCeyOYRe6GVoTa/3Z
+ Oy2iC3saEMGTRMwCV8kyxHbJiCmuQMR+RybBELJVkuy8YjH0jOvGfPKaYs0Y
+ XDopa5puyQ6FZ4q5nDyTU2hN+/xUr+0S0Y+DXM9TtDTVHOG82+4iKwdwiI8c
+ ZoiR5hHNMlTFrPbIpMIZPFfdl17tiR4+e6nu4D1OkE+ut07d4ktN8UjhwKRQ
+ xFEc4eYeo4WQueRKk7Pgb1eVPopU8oON8hEH5biDMqm+pth0kIOCJj2IGMIg
+ Fxhm2EYCtrNIUccqRcNeWBP0wRpDNU2x3W2KOIlRrolUh7I5RTYY9pdnvKiZ
+ xUJBNyxl9mxBMWxiRq5nlQJvRDDO8LTX362a3URetzeje043ug1Fnj2oa7ml
+ 7vIujOJMVdROkte1+ZSzfc/FcBoTRE1WzxeKFlFzIlHTp/Zy54qaDZwcUkfd
+ Zspv1zC8sXmk9EGfKX59/b5U+9shFCiRtNQYOsSwe4PVURZy2cnMDc6YFJ8M
+ I/WubQOO9M3i+DK0GTZ2rWslnRzeWs8ZirPYUF4x5ik0pmsb++jR8tZjAf8/
+ BRBlzACRwtCeyPieEbTJaXwwl2OIJ6rOrN4phnRVZ9pPfw2baHpwztDzdKyR
+ jnIMpupkrIa9hpLXF8mr+zc4V8pT4msF/af7nZGUYwylkJOzSpmN+ny9ejEE
+ FHOBbLr3JDYKN+LvlQ2EHjVsplZVXGeK+RleXJCPolRTucf/IgZjKOKTvFjY
+ GaMSZomXBHbrU/zQ3s1bn+GtHG+9LlJVeYS3Pici5bS+IOIFjiLgDREjGOWt
+ L1Ulo7VF5kZ70CscJnGBI36VYbhOxPXxpnGZ431DxBUH+VsMF+sKl61YfxWv
+ cB3fZThaX0j6gWQxy0F+IELFNd76oYgFxyE/Yji2CeOrK0QdBY7yE96ykX9G
+ 0ZHO5ux6mj9TER3NnJm8MHhmeETEO+jkZe6vGJ7b4vsCr4zdovy0YsmzsiXz
+ XJZfDNALC+OXENWpC9R1XeVPdEgKs4cYa3tw40BM6BBigtRD/2hMiPL2Hvrv
+ ozY9S0/QPUD3BrqHo/9+U+h4cONwUzwYFwYE5zrAhiJR4eE7YUEKnGqW4l3C
+ QMvhsLSD7sLJhzcZ9bW6fW1eX4fU3hXsYANChWTAxug4tU3q5PJc7nyPFOza
+ EZfiqzWGB0IvPbwZIvlwVzAakaLnO6RtXVF3tHIsRmNxqYFrqOgVpUaa0eTN
+ 2F4xJknNnB06BkB5TCkVzsEFuybtzuTzRYuX6eVacOS6pWgmr22fWqA8sPN8
+ UbPUvJLRFlVTJcnBcvFLdeekOq/JVtGg3Bgc1mcV/uKjaoqTSC5wZJ599ayc
+ m5INlT+7nY2TlpxdoLBwn2OTetHIKqMqf+h0lU6tUYlDFFsh+kfAo6yThxut
+ 7D0KgzDdnwOkOH/Tovb71CfgN0jQlV73qGeFetL0JNC9qe8OGvsC6btoeR/t
+ t23ZO3SN2aMS3SXcpZZI80gaHaSHc0gp0EV61kN6D+077+LD99Aj4JYtxWe2
+ OaPuTN7ai300xnjyrI3Ruz5Gn4tBaZfGOMYxF6O5jJEU8AC7lqtgmj2YZteU
+ Em0HbEl6yXLNStgqiAwOeQ9PswqTHEJiHiGU6xHwm/U8w3KNWY5rnq10G11T
+ tZA+uhFSugqJzhr/lXxsvZXQueSv/9RG+k9U6R/zwi1BiLb8fZy+FD97B+f/
+ WVM/nWKuS9M0K1BjVrcz4vkyhilcdJFewiVCcGx6kfroHHtkxJc9xI9T35XH
+ biOdfi4iZz9YA7HPGamBeNGO7xhmCDvgYss0Soeit+03Y62zO3iLbzeB2grm
+ XCsPk7RQA6nNtaOM9GpFKpqnFh3OW7KoerWMH+4u0uYs2ou8Hd0L0LbITd7j
+ Jm9bQgWCi/Q4PHjR3jH6Y+PpEzC25LkpiqWS50xM8A9lrkXn3YTb2v8v7Hob
+ ocCtvv4HKK7g+gpe4+klgHv2lW2zj5SwbZFUoafV09Pq8jnBP7LVgf/pTeP3
+ ufj9XrxU47PlMv5nl7dkvzTIP965+M+7+OG+/hXcWEbpaF2LE/Zwwh4PR+kU
+ cPyVdLNwqG8Fn/eHcVJoqOpYmeCfznxAvlg/SJpAhrxTpBLkzVt1gEzwL2Y+
+ Fny5fgtOEKeT+IprwcnSLuCu2nVgBV/zR/LbEaX9amdeQp3G12uhfnMrqC/b
+ qFdq2/rtrdt6Fd9xUacof/AM0uSg0v/gCr7nj+zkmsqyqYTcRMh5t6iyTwvS
+ kcX3XR0TruUtpQ1xoLQvbq63hBZPUYuXHluc9Ejwqgc/5sI3ektYwVvrATd6
+ wI3eChqdFRDwgv+Oe3tTO45wdPzYxbnkctxc5tg28qf+Me/QXFlWloxs9mhu
+ JprzNs36ujz8fIs8IIpfeNVWu60SaLgP4dId/PJd/Pq23VEqCgL4wL6/i/t0
+ X6Jpv6Vt97vLCGTw+wz+QFf8kV/+lKFC8c+XwUz8BX+9jC4TnSaW7V+Didul
+ 9jMmjpj4m4mUiSETL5gYNHHSHhoxMWryidTea2K/iT4T/SZ2mxj4H6im5hh+
+ GwAA
+ """,
+ """
+ stubs/MapSubclass.class:
+ H4sIAAAAAAAAALVY+1/b1hX/XvkFRhDZBAJkNCwliYFQk7Rpk9rLChkkDpAE
+ aElD1qTCCCqwJU+SWehe2aPt3u9lXffu3lu2sa0l7fb5bFl+3B+1z86VZNkY
+ GQzJPh9burr33O8593vOPedK//nv3/8J4CncY4iZVnHeTE7KhZnifDYnm2YE
+ jMGaWJZX5WRO1paSl+eXlayVcnqKlprj0ukKgYxmKUuKkfLpOpuaWNGtnKol
+ l1fzSZV6DU3OJfOysaIYZnKckArKwqT9mGKQqrVGEGRo3qQ5gjBDX92oETQw
+ hNOqplpnGQKJvlkRUTRFEYLI0OniZPVcjtSpumZzYY6T5haGBiVfsNaog4hK
+ 9G1mICVCQiyKfYgziL29C0pOWZIt5eYQw74tovvR1ggB7QxB6xXVZGid2MI8
+ EdCU1TVLVjVzXFkj0USmb45Ub+E1gg8wRFblXFG5vMjQTnI+5It4DIei6EYP
+ Q1tiq0f75kR04XATInicqFnhKlmG+C4ZMcsViDjmyCQYQrZKkl1SLIbeCd1Y
+ Si4r1rzBpZOypumW7JB4qZjLyfM5hdZ01E/11i4RAxjkep6gpanmKGfedhhZ
+ OYQTfOQkQ5Q0j2qWoSpmtU9mFIu0XanuS2/2RS+fvVZ3+J4lyMe3W6du8aWm
+ eKxwYFIo4jROcXPP0ELIXHKlyVnwt6tKH8Uq+cFG+ZCDctZBmVFfVWw6yEFB
+ kx5EjGCYC5xjaCQB21mkqGOTonNeYBP0YI2hmqbY7jZFXMAY10SqQ9mcIhsM
+ x8ozXtDMYqGgG5aycLmgGDYxo7eySoE3IphgeNLr71HNHiKvx5vRs6gbPYYi
+ LwzqWm6tp7wPG3CpKmpnyOvaUsrZwFeimMQUUZPV84WiRdScT9T0qb3cxaJm
+ AydH1DG3mfLbNQyv7R4pPegzxa9vwJdqfzuEwhDPE/5DJxgO7bA6ykMuO5nF
+ 4XmT4pNhtN617cCRvlscX4Z2w0b3tlZS7fDWesVQnMWG8oqxRKExV9vYh4+W
+ Nx8J+P8pgChjBogUhgOJjG+NoE1O48O5HEM8UVW1+mYZ0lWdaT/9NWyi6cFF
+ Q89TWSMd5RhM1clYDXsNJa+vkleP7VBXylPiWwX9p/vVSMoxhlLIyVmlzEZ9
+ vt68GAKKukA23YcTO4Ub8ffyDkIPGzazm85cl4r5eX64IB810KnKLf+rGI6i
+ iI/zw8LBKB1i1viRwG59ghftQ7z1Kd7K8dZnRDpZnuKtz4pIOa3Pi3iOowh4
+ TcQoxnjrjapktPWYudMe9A4OM3ieI36Z4VydiNvjzeE6x/uaiBsO8jcYrtYV
+ Lnux/iZe5jq+zXC6vpD0A8ligYN8T4SKZd76vogVxyE/YDizC+OrT4g6Chzl
+ R7xlI/+E4Zk9vgzQka30PjCpWPKCbMk8TeVXA/Q+wvglREfQFeq6pfInqn/C
+ wgnGpPu3j0eFDiEqSL30b4gKDbx9mP5HqU3P0mN0D9C9ie7hjvu3T7bEg3Fh
+ SHCuQ2wk9ODtsCAFLsakeJcw1HoyLO2nu3DhwR1GfW1uX7vX1yEd6Ap2sCGh
+ QjJgY3RcbJQ6uTyXm+6Vgl3741J8s77wUOjFB3e4znBXsCEiNUx3SI1dDe5o
+ 5ViUxuJSE9dQ0StKzTSjxZuxr2JMkmKcF8rtoOSklE7DwRX7oNmTyeeLFj97
+ lw94o7csRTP5gfWJFdrcjTPqkiZbRYNSWvCcvqDwNxZVU5z9/zyfy5OmnpVz
+ s7Kh8me38+B0UbPUvJLRVlVTpa7h8lmYXhxmLDm7QgHhSkdn9KKRVcZU/tDp
+ Tp3dMhEnKKpC9I+gEUyK85clWtmfKQAE/IxyDeNvbHRdp5409Ql0b+m/h+b+
+ QPo9tL6LA3+xZfk1ao/G6B7DX6kl0jySRgc6eYzxHOYiPe0hvYMDB9/DB99H
+ r4C7thSf2e6MujN56wiO0hjj2a82Rt/2GP0uBuVNGuMYZ1yMWBkjKeA+uter
+ YGIeTMw15W/UDtP9uC1Jb0muWQlbBZHBId/Hk6zCJIeQqEcIJWsE/GY962D5
+ zXJc87Sn/xlbLlUL6cM7IaWrkKhY+K/kI9uthAqLv/6LO+k/X6V/3Au3BCHa
+ 8v/A5LX45XuY/ldN/VSGXJemaVagxqweZ8TzZRSzuOoivYhrhODY9AL1USF6
+ aMSXPMSPUt+NR24jlS8XkbMfrIHY74zUQLxqx3cU84QdcLFlGqWq5m373Vjr
+ 7A7e4ttNoLaCRdfKkyQt1EBqd+0oI71SkYqWqEXVdU8WVa+W8ersIu3OoiPI
+ 29G9Am2P3OQ9bvK2JVThXaRH4cGr9o7RHxlPH4OxJ8/NUiyVPGdiin/pci2a
+ dhNu28C/0f0WQoG7/QP3UdzArQ28ytNLAO/YV9Zol5SwbVGsQk+bp6fN5XOK
+ fyWrA/+Tu8bvd/EHvHipxmfrZfxPr+/JfmmYf31z8Z918cP9Axu4vY5Sad2K
+ E/Zwwh4Pp6kKOP5Kulk41L+Bz/nDOCk0VFVWpvi3Lx+QL9QPkiaQEa+KVIK8
+ frcOkCn+ycvHgi/Wb8F54nQGX3ItuFDaBdxV3cc38BV/JL8dUdqvduYl1Dl8
+ tRbq1/eC+pKNeqO2rd/cu6038S0XdZbyB88gLQ4q/Qc38B1/ZCfXVB6bSsgt
+ hJx3D1V2tSAdWXzX1THlWt5a2hDHS/viznZLaPUUtXrpsdVJjwSvevDjLnyz
+ t4QNvLkdcLMH3OytoNlZAQGv+O+4t3a14whHxw9dnGsux7Eyx7aRP/aPeYfm
+ ymNlyciYR3OMaM7bNOvb8vDTPfLg1fsA3rXvf8IG3ddI5ue0o96+jkAGv8jg
+ l3TFr/jl1xn8Br+9Dmbid/j9dXSZ6DTxB/vXZOJuqf2UiVMm/mgiZWLExHMm
+ hk1csIdGTYyZfCK1j5g4ZqLfxICJQyaG/gcbVySQHhsAAA==
+ """,
+ """
+ stubs/SetObject.class:
+ H4sIAAAAAAAAAK1WW1cTVxT+zuQ2TAYJUUSw2lRRA6kG0Wo1FEHEGuSiJFKV
+ VjskYxyYzKQzE4r2Zm/28gN86GNXH32oq61iXaul9q0/qqv7TMYhhtDFcrkW
+ nHNmn7O/vfe39z4n//z7+x8AjuJ7hjbbqc7b6ZzqTM8vqAUnAsagTCwoS0pa
+ V4xSuibO1CRVR9P52cG6A1nDUUuqlRnKTCyajq4Z6YWlclojqWUoerqsWIuq
+ ZafPTyqVilqcdD8zDLFGExEEGVqfMxNBmKF306gRiAzhQc3QnCGGQLJ3VoaE
+ qIQQZIYuD6dg6jqZ00zDDds+T2a2MIhqueLcIgFDe7L3+XAzMmJol9CGOIPc
+ 01NUdbWkOOr1fiJw3dFt6GiBgO0MQeemZhPeRAPJFL5YMA1H0QzaDiazvVfp
+ 1DpKI3iFIbKk6FV1+gbDdjrXhHcZu/GqhF1IMHQk12eu96qMbuyJIoK9BEeu
+ l1WDomRZhugzL0Z0nRhK1sUy6tOU4d4dar43mFpvcMhV2DthWqX0gurMW9xA
+ WjEM01FqtE+ZzlRV1zkNnju2iBTD7mappjgtAtAKdgQHKcTCTbWw6CFcUCyl
+ rNJBhgNNQq+T5DhIKcNLIo1+CYdwWMYBJDktR4jb5pETX5o9xgvDrSdi8hiO
+ c5U3yXWN7CqOafHM1BdM1pOTdqrpRvPukXESGY79FlktqU5Ou626VilPQZs+
+ ZIxgmB84TWKlWKSY13AuGXa1UjEtRy1OV7gZcn9suaBW+CKCMYYjvjyh2QlK
+ RsLXSNwwrYSlKsWDpqHfSqx1iIi3G4qqjkcJWQlnMU49R964FZTeRJWsRczL
+ JFTQVYUoDFtq2VyigOPr08jQUtt1bdCal6y7rrtEpqrled4yOZ4Zw5nlXSPj
+ EoYl5DHLW2CnRF15WcYpLhNAyTxTk71LhDvmiGUplOZkcq5JDzWRMSQG8yeb
+ lH9yLp8nDRooPIWjilAYtjVjJoICw75mZb92Jl9zLQJiZ2iDFt2kzzKuoSTh
+ Bm4ybG0aU0eyeaypje6GDazo3EqZcjFY0N0LmRNNt7CYncrlR6ZGx2R8gC5+
+ T1LyezbzuvB71CNqkiqgqDgKyYTyUoDeMsaHEF1qiyRa1vgX3c1C8TDDT6t3
+ eiRhhyAJsT30L0qCGKI5SnOA5t00872k+PddYcfqnQExHowL/UI/Ox0Rhac/
+ hoVYYLw9FukW+sWBcKyFZuHc03uB8c6Y5MrkWLT7mY5MO2y8JdbKd9x1W2yL
+ v9vG9WbisSDHuPz0XojQw91BMRQLc18HGMWBRLZcrjrKvK6usTy27Kh0A9LV
+ eWiRbu6dM1XD0cpq1ljSbI1OjqzdrdQfOa1kKE7VonIJjppFlb9RmqHWGiTP
+ kXmXmQVFn1UsjX97wp5GXP96fc5Aa85RCov08HpqUs6sWgX1rMY/ujyM2XWe
+ 4TAlO0T/EfC0d/H8U7yfU6rCNPcBsTh/O2n9BckEOIjTSA84Sb4kySB9CTRv
+ 6XuE1r7A4Aq2/obOB+7Zr2iU3N2oq/E1rWTSo9PYQXY4s3QDeEjHfKRf0blz
+ Ba89Ro+A++4prrm9tutp8tU+7Kc9xl8MBFyMEx5Geyr++goGOFLqMY6uh2n3
+ YdqRwht+eL24S3OE1WKnkZ4Wz7+ka4si4qiPcYLVgdaikvyo6NHwPGrQop9A
+ P2+gVXNgsJ57GulebG5/9P/sn/FZHSA/eMDSE5y9Ej/3COf/bKBC8qmQfEYn
+ yJrwAvr1VE7SasqvE54htgFOo/fT2POC3l9wvb/4ErzvpdXMS8G5yH/oefHM
+ ePXZkfoLu35AKHC/L7WK/EO88xBXeDoD+MYdWYvbPWGXmWidnQ7fTocX70X+
+ C8TDT3s1Eup7iLn7fhPWw9TIDtVgYiNUKs+cG/ack9d8eq85Rs0V2XdF9qm/
+ huseZcc9tLZUfJ634iqKqRVoa9VfA2nzQdqgYMHn7X0XbNFzbb8Xl8hhVmA0
+ 9pDooYgw/YrrdPHJ5ScQrjxC5RfYD1xBwDMRwLfufAff0TxHalXiZWkOgSw+
+ zGKZRtziw+0sPsLHdMDGJ/h0DjEbXTY+c/+iNkZsnLIxbKPbleyzccBGyl0f
+ s3GSFv8BceJNatwNAAA=
+ """,
+ """
+ stubs/SetSubclass.class:
+ H4sIAAAAAAAAAK1W3VcTRxT/zeaTZZEQBQWrTRU1kGoQrVZDEVSsQT4UkKq0
+ 2iFZ48JmN93dULRf9Otf8KEPfeizD+05rWJ7Tkt97B/V0zubZRPJ0sPxeA7M
+ zN6Z+7v3/ubeO/nn39//BHAa3zN02E510c7Oqs5sdbGgc9uOgTHwiSW+wrM6
+ N0rZ6cUlteDkapKqo+ni9FDDgbzhqCXVyg3nJpZNR9eM7NJKOauR1DK4ni1z
+ a1m17Oy1SV6pqMVJ9zPHkNhqIoYwQ9tLZmKIMvTtGDWGOEN0SDM0Z5ghlO6b
+ VyCjVUYECkO3h1MwdZ3MaabhBm5fIzO7GOJqueI8JAGxku57OdycggQ6ZLQj
+ yaD09hZVXS1xR703wNDedHQPOlsgoYsh7DzQbIbdE000EwHxgmk4XDPoQDid
+ 77tDdptIjeENhtgK16vq9H2GLjoXwLyCg3hTxgGkGDrTzXfXd0dBDw61IobD
+ BEfOl1WD4mR5htZNL0Z1nThKN0RzyScqJ7w7Ebw3lGk2OOwqHJ4wrVJ2SXUW
+ LWEgyw3DdHiN+CnTmarquqDBc8eOI8NwMOiyKU6LALQCZedxCrHwQC0sewjX
+ ucXLKh1kOBYQeoNkVoCUciIpshiQcQInFRxDWtByirgNjpz40uwxkRpuRhGT
+ Z3BWqLxLrmtklzumJW6mMWXynpy0M4EbwfWj4DxyAvs9slqiZNEeqa5Vuqew
+ TR8KRjEiDlwkMS8WKeY6zk3DrlYqpuWoxemKMEPuj60W1IpYxDDGcMqXpzQ7
+ RZeR8jVS900rZam8eNw09Iepeo3E8f6WpGrgUUZexhWMU9WRN24GZXeQJfWI
+ RZpECrrKicKopZbNFQo42XyNDC21XdcGrUXKuuuGNjJVLS+KkpkVN2M486Jq
+ FNzEiIw5zIsS2C9TXd5ScEHIJNBlXq7JPiTCHXPUsjhdczq9EFBDATKG1NDc
+ +YD0Ty/MzZEGDRQeF6hxcIY9QczEUGA4EpT29TNzNddiIHaGtynRHfqs4C5K
+ Mu7jAfWlwJg608GxZrbrDdtY0YWVMkPvTp4Oan6br8ckXW6RO5xkUnklRE8V
+ E0OE+tUyiVY18UWNVyqeZPhxY61XlvZJspQ4RP9xWYpHaG6lOUTzQZrFXnrf
+ xtpgPBlOSgPSALsYefFTVEqExjsSsR5pID4YTbTQLF198Tg0vjchuzIl0dqz
+ qaHQDhtvSbSJHXfdntjl77YLvZlkIiwwbr14LNCjPeF4JBEVXg4yigCpfLlc
+ dfiirtapG1t1VGpr1A9PLFM7bpnVSgZ3qhbdcviSWVTF46IZai2v54SuKA6z
+ wPV5bmni2xP2zlQNRyureWNFszUS+V1xtN5zGfZvPfbSbtuswwvL9J56oPKs
+ WbUK6hVNfHR7qvNNijhJ9ROh/xhawBJJ8fxRvJ/TJUkw0UFreoNp/IIkQyST
+ aN7V/wxt/aGhdez+DXt/cc9+SaPs7orGouArd8XEaexDt8gDUcIe0hkf6Vfs
+ 3b+Ot56jV8IT95TQ7KrteppidQRHaY+Jlo+Qi3HOw+jIJN9ex6BAyjzH6WaY
+ Dh+mAxm844fXhzWaYwK131Wht8HzL+3aoogE6nOcYw2gtahkPyrq+p5HW7To
+ V8zP22jVHBjC1zRHffvU2ILtX/o/+5d9VgfJDxGw/Aeu3E5efYZrf22hQvap
+ kH1GJ8ia9Ar6jVRO0mrKzxNxQ2wbnK3eT+PQK3p/3fX+xmvwvo9WM68F54b4
+ pebFM+PlZ2fmbxz4AZHQk/7MBuae4oOnuC2uM4Rv3JG1uNUTdZlRGux0+nY6
+ vXhviJ8QHn7Wy5FI/1MsPPGLsBGmRnakBpMYpVTZdG7Ec06p+/RRMEaXV9Sb
+ rig+9Xdxz6PsrIfWnkkuilLcQDGzDq2e/TWQdh+kHRxLPm8fu2DLnmtHvbji
+ AmYdxtYainsoIU87hG/d+TN8R/MCnalQyJ8sIJSHlYdNIxwxVPNYwad0wMYq
+ Hi4gYaPbxiP3r9XGqI0LNkZs9LiSIzaO2ci46zM2ztPiPxBMeXd8DQAA
+ """
+ )
+
+ @Test
+ fun mutableCollection_stdlib() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.runtime.*
+
+ val list = mutableListOf(1)
+ val set = mutableSetOf(1)
+ val map = mutableMapOf(1 to 1)
+ val collection: MutableCollection<Int> = list
+
+ val listFunction = mutableStateOf(mutableListOf(1))
+ val listProperty = mutableStateOf(list)
+
+ val setFunction = mutableStateOf(mutableSetOf(1))
+ val setProperty = mutableStateOf(set)
+
+ val mapFunction = mutableStateOf(mutableMapOf(1 to 1))
+ val mapProperty = mutableStateOf(map)
+
+ val collectionProperty = mutableStateOf(collection)
+
+ fun test(
+ listParam: MutableList<Int>,
+ setParam: MutableSet<Int>,
+ mapParam: MutableMap<Int, Int>,
+ collectionParam: MutableCollection<Int>
+ ) {
+ val listParameter = mutableStateOf(listParam)
+ val setParameter = mutableStateOf(setParam)
+ val mapParameter = mutableStateOf(mapParam)
+ val collectionProperty = mutableStateOf(collectionParam)
+ }
+ """
+ ),
+ Stubs.SnapshotState
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expect(
+ """
+ src/test/test.kt:11: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listFunction = mutableStateOf(mutableListOf(1))
+ ~~~~~~~~~~~~~~
+src/test/test.kt:12: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listProperty = mutableStateOf(list)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:14: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setFunction = mutableStateOf(mutableSetOf(1))
+ ~~~~~~~~~~~~~~
+src/test/test.kt:15: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setProperty = mutableStateOf(set)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:17: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapFunction = mutableStateOf(mutableMapOf(1 to 1))
+ ~~~~~~~~~~~~~~
+src/test/test.kt:18: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapProperty = mutableStateOf(map)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:20: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionProperty = mutableStateOf(collection)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:28: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listParameter = mutableStateOf(listParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:29: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setParameter = mutableStateOf(setParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:30: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapParameter = mutableStateOf(mapParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:31: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionProperty = mutableStateOf(collectionParam)
+ ~~~~~~~~~~~~~~
+0 errors, 11 warnings
+ """
+ )
+ }
+
+ @Test
+ fun mutableCollection_stdlib_explicitExpressionType_noErrors() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.runtime.*
+
+ val list = mutableListOf(1)
+ val set = mutableSetOf(1)
+ val map = mutableMapOf(1 to 1)
+ val collection: MutableCollection<Int> = list
+
+ val listFunction = mutableStateOf<List<Int>>(mutableListOf(1))
+ val listProperty = mutableStateOf<List<Int>>(list)
+
+ val setFunction = mutableStateOf<Set<Int>>(mutableSetOf(1))
+ val setProperty = mutableStateOf<Set<Int>>(set)
+
+ val mapFunction = mutableStateOf<Map<Int, Int>>(mutableMapOf(1 to 1))
+ val mapProperty = mutableStateOf<Map<Int, Int>>(map)
+
+ val collectionProperty = mutableStateOf<Collection<Int>>(collection)
+
+ fun test(
+ listParam: MutableList<Int>,
+ setParam: MutableSet<Int>,
+ mapParam: MutableMap<Int, Int>,
+ collectionParam: MutableCollection<Int>
+ ) {
+ val listParameter = mutableStateOf<List<Int>>(listParam)
+ val setParameter = mutableStateOf<Set<Int>>(setParam)
+ val mapParameter = mutableStateOf<Map<Int, Int>>(mapParam)
+ val collectionProperty = mutableStateOf<Collection<Int>>(collectionParam)
+ }
+ """
+ ),
+ Stubs.SnapshotState
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun mutableCollection_stdlib_explicitPropertyType_noErrors() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.runtime.*
+
+ val list = mutableListOf(1)
+ val set = mutableSetOf(1)
+ val map = mutableMapOf(1 to 1)
+ val collection: MutableCollection<Int> = list
+
+ val listFunction: MutableState<List<Int>> = mutableStateOf(mutableListOf(1))
+ val listProperty: MutableState<List<Int>> = mutableStateOf(list)
+
+ val setFunction: MutableState<Set<Int>> = mutableStateOf(mutableSetOf(1))
+ val setProperty: MutableState<Set<Int>> = mutableStateOf(set)
+
+ val mapFunction: MutableState<Map<Int, Int>> = mutableStateOf(mutableMapOf(1 to 1))
+ val mapProperty: MutableState<Map<Int, Int>> = mutableStateOf(map)
+
+ val collectionProperty: MutableState<Collection<Int>> = mutableStateOf(collection)
+
+ fun test(
+ listParam: MutableList<Int>,
+ setParam: MutableSet<Int>,
+ mapParam: MutableMap<Int, Int>,
+ collectionParam: MutableCollection<Int>
+ ) {
+ val listParameter: MutableState<List<Int>> = mutableStateOf(listParam)
+ val setParameter: MutableState<Set<Int>> = mutableStateOf(setParam)
+ val mapParameter: MutableState<Map<Int, Int>> = mutableStateOf(mapParam)
+ val collectionProperty: MutableState<Collection<Int>> = mutableStateOf(collectionParam)
+ }
+ """
+ ),
+ Stubs.SnapshotState
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun mutableCollection_java() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.runtime.*
+
+ val list = java.util.ArrayList<Int>()
+ val set = java.util.HashSet<Int>()
+ val linkedSet = java.util.LinkedHashSet<Int>()
+ val map = java.util.HashMap<Int, Int>()
+ val linkedMap = java.util.LinkedHashMap<Int, Int>()
+ val collection: java.util.Collection<Int> = list as java.util.Collection<Int>
+
+ val listFunction = mutableStateOf(java.util.ArrayList<Int>())
+ val listProperty = mutableStateOf(list)
+
+ val setFunction = mutableStateOf(java.util.HashSet<Int>())
+ val setProperty = mutableStateOf(set)
+
+ val linkedSetFunction = mutableStateOf(java.util.LinkedHashSet<Int>())
+ val linkedSetProperty = mutableStateOf(linkedSet)
+
+ val mapFunction = mutableStateOf(java.util.HashMap<Int, Int>())
+ val mapProperty = mutableStateOf(map)
+
+ val linkedMapFunction = mutableStateOf(java.util.LinkedHashMap<Int, Int>())
+ val linkedMapProperty = mutableStateOf(linkedMap)
+
+ val collectionProperty = mutableStateOf(collection)
+
+ fun test(
+ listParam: java.util.ArrayList<Int>,
+ setParam: java.util.HashSet<Int>,
+ linkedSetParam: java.util.LinkedHashSet<Int>,
+ mapParam: java.util.HashMap<Int, Int>,
+ linkedMapParam: java.util.LinkedHashMap<Int, Int>,
+ collectionParam: java.util.Collection<Int>
+ ) {
+ val listParameter = mutableStateOf(listParam)
+ val setParameter = mutableStateOf(setParam)
+ val linkedSetParameter = mutableStateOf(linkedSetParam)
+ val mapParameter = mutableStateOf(mapParam)
+ val linkedMapParameter = mutableStateOf(linkedMapParam)
+ val collectionProperty = mutableStateOf(collectionParam)
+ }
+ """
+ ),
+ Stubs.SnapshotState
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expect(
+ """
+src/test/test.kt:13: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listFunction = mutableStateOf(java.util.ArrayList<Int>())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:14: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listProperty = mutableStateOf(list)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:16: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setFunction = mutableStateOf(java.util.HashSet<Int>())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:17: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setProperty = mutableStateOf(set)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:19: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedSetFunction = mutableStateOf(java.util.LinkedHashSet<Int>())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:20: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedSetProperty = mutableStateOf(linkedSet)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:22: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapFunction = mutableStateOf(java.util.HashMap<Int, Int>())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:23: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapProperty = mutableStateOf(map)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:25: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedMapFunction = mutableStateOf(java.util.LinkedHashMap<Int, Int>())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:26: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedMapProperty = mutableStateOf(linkedMap)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:28: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionProperty = mutableStateOf(collection)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:38: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listParameter = mutableStateOf(listParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:39: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setParameter = mutableStateOf(setParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:40: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedSetParameter = mutableStateOf(linkedSetParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:41: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapParameter = mutableStateOf(mapParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:42: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedMapParameter = mutableStateOf(linkedMapParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:43: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionProperty = mutableStateOf(collectionParam)
+ ~~~~~~~~~~~~~~
+0 errors, 17 warnings
+ """
+ )
+ }
+
+ /**
+ * Tests for Kotlin collection types that are actually just aliases for the java classes on JVM
+ */
+ @Test
+ fun mutableCollection_kotlinTypeAliases() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.runtime.*
+
+ val list = kotlin.collections.ArrayList<Int>()
+ val set = kotlin.collections.HashSet<Int>()
+ val linkedSet = kotlin.collections.LinkedHashSet<Int>()
+ val map = kotlin.collections.HashMap<Int, Int>()
+ val linkedMap = kotlin.collections.LinkedHashMap<Int, Int>()
+
+ val listFunction = mutableStateOf(kotlin.collections.ArrayList<Int>())
+ val listProperty = mutableStateOf(list)
+
+ val setFunction = mutableStateOf(kotlin.collections.HashSet<Int>())
+ val setProperty = mutableStateOf(set)
+
+ val linkedSetFunction = mutableStateOf(kotlin.collections.LinkedHashSet<Int>())
+ val linkedSetProperty = mutableStateOf(linkedSet)
+
+ val mapFunction = mutableStateOf(kotlin.collections.HashMap<Int, Int>())
+ val mapProperty = mutableStateOf(map)
+
+ val linkedMapFunction = mutableStateOf(kotlin.collections.LinkedHashMap<Int, Int>())
+ val linkedMapProperty = mutableStateOf(linkedMap)
+
+ fun test(
+ listParam: kotlin.collections.ArrayList<Int>,
+ setParam: kotlin.collections.HashSet<Int>,
+ linkedSetParam: kotlin.collections.LinkedHashSet<Int>,
+ mapParam: kotlin.collections.HashMap<Int, Int>,
+ linkedMapParam: kotlin.collections.LinkedHashMap<Int, Int>,
+ ) {
+ val listParameter = mutableStateOf(listParam)
+ val setParameter = mutableStateOf(setParam)
+ val linkedSetParameter = mutableStateOf(linkedSetParam)
+ val mapParameter = mutableStateOf(mapParam)
+ val linkedMapParameter = mutableStateOf(linkedMapParam)
+ }
+ """
+ ),
+ Stubs.SnapshotState
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expect(
+ """
+ src/test/test.kt:12: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listFunction = mutableStateOf(kotlin.collections.ArrayList<Int>())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:13: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listProperty = mutableStateOf(list)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:15: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setFunction = mutableStateOf(kotlin.collections.HashSet<Int>())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:16: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setProperty = mutableStateOf(set)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:18: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedSetFunction = mutableStateOf(kotlin.collections.LinkedHashSet<Int>())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:19: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedSetProperty = mutableStateOf(linkedSet)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:21: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapFunction = mutableStateOf(kotlin.collections.HashMap<Int, Int>())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:22: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapProperty = mutableStateOf(map)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:24: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedMapFunction = mutableStateOf(kotlin.collections.LinkedHashMap<Int, Int>())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:25: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedMapProperty = mutableStateOf(linkedMap)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:34: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listParameter = mutableStateOf(listParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:35: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setParameter = mutableStateOf(setParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:36: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedSetParameter = mutableStateOf(linkedSetParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:37: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapParameter = mutableStateOf(mapParam)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:38: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val linkedMapParameter = mutableStateOf(linkedMapParam)
+ ~~~~~~~~~~~~~~
+0 errors, 15 warnings
+ """
+ )
+ }
+
+ @Test
+ fun mutableCollection_sourceExtensions() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.runtime.*
+ import stubs.*
+
+ val listFunction = mutableStateOf(mutableList())
+ val listProperty = mutableStateOf(MutableList)
+ val listObjectImplementation = mutableStateOf(MutableListObject)
+ val listSubclass = mutableStateOf(MutableListSubclass())
+
+ val setFunction = mutableStateOf(mutableSet())
+ val setProperty = mutableStateOf(MutableSet)
+ val setObjectImplementation = mutableStateOf(MutableSetObject)
+ val setSubclass = mutableStateOf(MutableSetSubclass())
+
+ val mapFunction = mutableStateOf(mutableMap())
+ val mapProperty = mutableStateOf(MutableMap)
+ val mapObjectImplementation = mutableStateOf(MutableMapObject)
+ val mapSubclass = mutableStateOf(MutableMapSubclass())
+
+ val collectionFunction = mutableStateOf(mutableCollection())
+ val collectionProperty = mutableStateOf(MutableCollection)
+ val collectionObjectImplementation = mutableStateOf(MutableCollectionObject)
+ val collectionSubclass = mutableStateOf(MutableCollectionSubclass())
+ """
+ ),
+ Stubs.SnapshotState,
+ KotlinMutableCollectionExtensions.kotlin
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expect(
+ """
+ src/test/test.kt:7: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listFunction = mutableStateOf(mutableList())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:8: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listProperty = mutableStateOf(MutableList)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:9: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listObjectImplementation = mutableStateOf(MutableListObject)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:10: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listSubclass = mutableStateOf(MutableListSubclass())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:12: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setFunction = mutableStateOf(mutableSet())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:13: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setProperty = mutableStateOf(MutableSet)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:14: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setObjectImplementation = mutableStateOf(MutableSetObject)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:15: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setSubclass = mutableStateOf(MutableSetSubclass())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:17: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapFunction = mutableStateOf(mutableMap())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:18: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapProperty = mutableStateOf(MutableMap)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:19: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapObjectImplementation = mutableStateOf(MutableMapObject)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:20: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapSubclass = mutableStateOf(MutableMapSubclass())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:22: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionFunction = mutableStateOf(mutableCollection())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:23: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionProperty = mutableStateOf(MutableCollection)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:24: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionObjectImplementation = mutableStateOf(MutableCollectionObject)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:25: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionSubclass = mutableStateOf(MutableCollectionSubclass())
+ ~~~~~~~~~~~~~~
+0 errors, 16 warnings
+ """
+ )
+ }
+
+ @Test
+ fun mutableCollection_compiledExtensions() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.runtime.*
+ import stubs.*
+
+ val listFunction = mutableStateOf(mutableList())
+ val listProperty = mutableStateOf(MutableList)
+ val listObjectImplementation = mutableStateOf(MutableListObject)
+ val listSubclass = mutableStateOf(MutableListSubclass())
+
+ val setFunction = mutableStateOf(mutableSet())
+ val setProperty = mutableStateOf(MutableSet)
+ val setObjectImplementation = mutableStateOf(MutableSetObject)
+ val setSubclass = mutableStateOf(MutableSetSubclass())
+
+ val mapFunction = mutableStateOf(mutableMap())
+ val mapProperty = mutableStateOf(MutableMap)
+ val mapObjectImplementation = mutableStateOf(MutableMapObject)
+ val mapSubclass = mutableStateOf(MutableMapSubclass())
+
+ val collectionFunction = mutableStateOf(mutableCollection())
+ val collectionProperty = mutableStateOf(MutableCollection)
+ val collectionObjectImplementation = mutableStateOf(MutableCollectionObject)
+ val collectionSubclass = mutableStateOf(MutableCollectionSubclass())
+ """
+ ),
+ Stubs.SnapshotState,
+ Stubs.Composable,
+ KotlinMutableCollectionExtensions.compiled
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expect(
+ """
+ src/test/test.kt:7: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listFunction = mutableStateOf(mutableList())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:8: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listProperty = mutableStateOf(MutableList)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:9: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listObjectImplementation = mutableStateOf(MutableListObject)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:10: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val listSubclass = mutableStateOf(MutableListSubclass())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:12: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setFunction = mutableStateOf(mutableSet())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:13: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setProperty = mutableStateOf(MutableSet)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:14: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setObjectImplementation = mutableStateOf(MutableSetObject)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:15: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val setSubclass = mutableStateOf(MutableSetSubclass())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:17: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapFunction = mutableStateOf(mutableMap())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:18: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapProperty = mutableStateOf(MutableMap)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:19: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapObjectImplementation = mutableStateOf(MutableMapObject)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:20: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val mapSubclass = mutableStateOf(MutableMapSubclass())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:22: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionFunction = mutableStateOf(mutableCollection())
+ ~~~~~~~~~~~~~~
+src/test/test.kt:23: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionProperty = mutableStateOf(MutableCollection)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:24: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionObjectImplementation = mutableStateOf(MutableCollectionObject)
+ ~~~~~~~~~~~~~~
+src/test/test.kt:25: Warning: Creating a MutableState object with a mutable collection type [MutableCollectionMutableState]
+ val collectionSubclass = mutableStateOf(MutableCollectionSubclass())
+ ~~~~~~~~~~~~~~
+0 errors, 16 warnings
+ """
+ )
+ }
+
+ @Test
+ fun immutableCollection_stdlib_noErrors() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.runtime.*
+
+ val list = listOf(1)
+ val set = setOf(1)
+ val map = mapOf(1 to 1)
+ val collection: Collection<Int> = list
+
+ val listFunction = mutableStateOf(listOf(1))
+ val listProperty = mutableStateOf(list)
+
+ val setFunction = mutableStateOf(setOf(1))
+ val setProperty = mutableStateOf(set)
+
+ val mapFunction = mutableStateOf(mapOf(1 to 1))
+ val mapProperty = mutableStateOf(map)
+
+ val collectionProperty = mutableStateOf(collection)
+
+ fun test(
+ listParam: List<Int>,
+ setParam: Set<Int>,
+ mapParam: Map<Int, Int>,
+ collectionParam: Collection<Int>
+ ) {
+ val listParameter = mutableStateOf(listParam)
+ val setParameter = mutableStateOf(setParam)
+ val mapParameter = mutableStateOf(mapParam)
+ val collectionProperty = mutableStateOf(collectionParam)
+ }
+ """
+ ),
+ Stubs.SnapshotState
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun immutableCollection_sourceExtensions_noErrors() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.runtime.*
+ import stubs.*
+
+ val listFunction = mutableStateOf(list())
+ val listProperty = mutableStateOf(List)
+ val listObjectImplementation = mutableStateOf(ListObject)
+ val listSubclass = mutableStateOf(ListSubclass())
+
+ val setFunction = mutableStateOf(set())
+ val setProperty = mutableStateOf(Set)
+ val setObjectImplementation = mutableStateOf(SetObject)
+ val setSubclass = mutableStateOf(SetSubclass())
+
+ val mapFunction = mutableStateOf(map())
+ val mapProperty = mutableStateOf(Map)
+ val mapObjectImplementation = mutableStateOf(MapObject)
+ val mapSubclass = mutableStateOf(MapSubclass())
+
+ val collectionFunction = mutableStateOf(collection())
+ val collectionProperty = mutableStateOf(Collection)
+ val collectionObjectImplementation = mutableStateOf(CollectionObject)
+ val collectionSubclass = mutableStateOf(CollectionSubclass())
+ """
+ ),
+ Stubs.SnapshotState,
+ KotlinImmutableCollectionExtensions.kotlin
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun immutableCollection_compiledExtensions_noErrors() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.runtime.*
+ import stubs.*
+
+ val listFunction = mutableStateOf(list())
+ val listProperty = mutableStateOf(List)
+ val listObjectImplementation = mutableStateOf(ListObject)
+ val listSubclass = mutableStateOf(ListSubclass())
+
+ val setFunction = mutableStateOf(set())
+ val setProperty = mutableStateOf(Set)
+ val setObjectImplementation = mutableStateOf(SetObject)
+ val setSubclass = mutableStateOf(SetSubclass())
+
+ val mapFunction = mutableStateOf(map())
+ val mapProperty = mutableStateOf(Map)
+ val mapObjectImplementation = mutableStateOf(MapObject)
+ val mapSubclass = mutableStateOf(MapSubclass())
+
+ val collectionFunction = mutableStateOf(collection())
+ val collectionProperty = mutableStateOf(Collection)
+ val collectionObjectImplementation = mutableStateOf(CollectionObject)
+ val collectionSubclass = mutableStateOf(CollectionSubclass())
+ """
+ ),
+ Stubs.SnapshotState,
+ Stubs.Composable,
+ KotlinImmutableCollectionExtensions.compiled
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expectClean()
+ }
+}
+/* ktlint-enable max-line-length */
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/ListUtils.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/ListUtils.kt
index 2e6554c..cb0552e 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/ListUtils.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/ListUtils.kt
@@ -22,6 +22,10 @@
/**
* Iterates through a [List] using the index and calls [action] for each item.
* This does not allocate an iterator like [Iterable.forEach].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastForEach(action: (T) -> Unit) {
@@ -36,6 +40,10 @@
* Returns a [Set] of all elements.
*
* The returned set preserves the element iteration order of the original collection.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
internal fun <T> List<T>.fastToSet(): Set<T> = HashSet<T>(size).also { set ->
fastForEach { item -> set.add(item) }
@@ -44,6 +52,10 @@
/**
* Iterates through a [List] using the index and calls [action] for each item.
* This does not allocate an iterator like [Iterable.forEachIndexed].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastForEachIndexed(action: (Int, T) -> Unit) {
@@ -57,6 +69,10 @@
/**
* Returns a list containing the results of applying the given [transform] function
* to each element in the original collection.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R> List<T>.fastMap(transform: (T) -> R): List<R> {
@@ -75,6 +91,10 @@
* If the collection could be huge, you can specify a non-negative value of [limit], in which case
* only the first [limit] elements will be appended, followed by the [truncated] string (which
* defaults to "...").
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
internal fun <T> List<T>.fastJoinToString(
separator: CharSequence = ", ",
@@ -95,6 +115,10 @@
* If the collection could be huge, you can specify a non-negative value of [limit], in which
* case only the first [limit] elements will be appended, followed by the [truncated] string
* (which defaults to "...").
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
private fun <T, A : Appendable> List<T>.fastJoinTo(
buffer: A,
@@ -134,6 +158,10 @@
/**
* Returns a list containing the results of applying the given [transform] function
* to each element in the original collection.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R> List<T>.fastMapNotNull(transform: (T) -> R?): List<R> {
@@ -149,6 +177,10 @@
* Returns a list containing only elements matching the given [predicate].
* @param [predicate] function that takes the index of an element and the element itself
* and returns the result of predicate evaluation on the element.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastFilterIndexed(predicate: (index: Int, T) -> Boolean): List<T> {
diff --git a/compose/test-utils/OWNERS b/compose/test-utils/OWNERS
index 305021a..b2c0d56 100644
--- a/compose/test-utils/OWNERS
+++ b/compose/test-utils/OWNERS
@@ -1,2 +1 @@
-pavlis@google.com
jellefresen@google.com
diff --git a/compose/ui/ui-inspection/src/androidTest/AndroidManifest.xml b/compose/ui/ui-inspection/src/androidTest/AndroidManifest.xml
index bc217cf..48da372 100644
--- a/compose/ui/ui-inspection/src/androidTest/AndroidManifest.xml
+++ b/compose/ui/ui-inspection/src/androidTest/AndroidManifest.xml
@@ -17,6 +17,7 @@
<application>
<activity android:name="androidx.compose.ui.inspection.testdata.AndroidViewTestActivity" />
+ <activity android:name="androidx.compose.ui.inspection.testdata.ComposeViewTestActivity" />
<activity android:name="androidx.compose.ui.inspection.testdata.DialogTestActivity" />
<activity android:name="androidx.compose.ui.inspection.testdata.ParametersTestActivity" />
<activity android:name="androidx.compose.ui.inspection.testdata.RippleTestActivity" />
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ComposeViewTest.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ComposeViewTest.kt
new file mode 100644
index 0000000..9f6366a
--- /dev/null
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/ComposeViewTest.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.compose.ui.inspection
+
+import androidx.compose.ui.inspection.rules.ComposeInspectionRule
+import androidx.compose.ui.inspection.rules.sendCommand
+import androidx.compose.ui.inspection.testdata.ComposeViewTestActivity
+import androidx.compose.ui.inspection.util.GetComposablesCommand
+import androidx.compose.ui.inspection.util.GetParametersCommand
+import androidx.compose.ui.inspection.util.flatten
+import androidx.compose.ui.inspection.util.toMap
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.ComposableNode
+import org.junit.Rule
+import org.junit.Test
+
+@LargeTest
+class ComposeViewTest {
+ @get:Rule
+ val rule = ComposeInspectionRule(ComposeViewTestActivity::class)
+
+ @Test
+ fun composeView(): Unit = runBlocking {
+ val response = rule.inspectorTester.sendCommand(
+ GetComposablesCommand(rule.rootId, skipSystemComposables = false)
+ ).getComposablesResponse
+ val strings = response.stringsList.toMap()
+ val roots = response.rootsList
+ assertThat(roots).hasSize(3)
+ val firstText = roots[0].nodesList.findNode("Text", strings)
+ val secondText = roots[1].nodesList.findNode("Text", strings)
+ val thirdText = roots[2].nodesList.findNode("Text", strings)
+ assertThat(firstText?.textParameter).isEqualTo("one")
+ assertThat(secondText?.textParameter).isEqualTo("two")
+ assertThat(thirdText?.textParameter).isEqualTo("three")
+ }
+
+ private fun Iterable<ComposableNode>.findNode(
+ name: String,
+ strings: Map<Int, String>
+ ): ComposableNode? = flatMap { it.flatten() }.singleOrNull { strings[it.name] == name }
+
+ private val ComposableNode.textParameter: String?
+ get() = runBlocking {
+ val params = rule.inspectorTester.sendCommand(
+ GetParametersCommand(
+ rule.rootId,
+ skipSystemComposables = false,
+ composableId = id
+ )
+ ).getParametersResponse
+ val strings = params.stringsList.toMap()
+ val param = params.parameterGroup.parameterList.single { strings[it.name] == "text" }
+ strings[param.int32Value]
+ }
+}
diff --git a/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/ComposeViewTestActivity.kt b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/ComposeViewTestActivity.kt
new file mode 100644
index 0000000..3c36a5a
--- /dev/null
+++ b/compose/ui/ui-inspection/src/androidTest/java/androidx/compose/ui/inspection/testdata/ComposeViewTestActivity.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.compose.ui.inspection.testdata
+
+import android.os.Bundle
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material.Text
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.viewinterop.AndroidView
+
+class ComposeViewTestActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ Column {
+ Text("one")
+ AndroidView({ context ->
+ LinearLayout(context).apply {
+ orientation = LinearLayout.VERTICAL
+ addView(TextView(context).apply { text = "AndroidView" })
+ addView(ComposeView(context).apply {
+ setContent {
+ Column {
+ Text("two")
+ AndroidView({ context ->
+ LinearLayout(context).apply {
+ orientation = LinearLayout.VERTICAL
+ addView(ComposeView(context).apply {
+ setContent {
+ Text("three")
+ }
+ })
+ }
+ })
+ }
+ }
+ })
+ }
+ })
+ }
+ }
+ }
+}
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
index 6999d46..af7dab6 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/ComposeLayoutInspector.kt
@@ -27,7 +27,7 @@
import androidx.compose.ui.inspection.inspector.NodeParameterReference
import androidx.compose.ui.inspection.proto.StringTable
import androidx.compose.ui.inspection.proto.convert
-import androidx.compose.ui.inspection.proto.toComposableNodes
+import androidx.compose.ui.inspection.proto.toComposableRoot
import androidx.compose.ui.inspection.util.ThreadUtils
import androidx.compose.ui.unit.IntOffset
import androidx.inspection.Connection
@@ -37,7 +37,6 @@
import com.google.protobuf.ByteString
import com.google.protobuf.InvalidProtocolBufferException
import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.Command
-import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.ComposableRoot
import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.GetAllParametersCommand
import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.GetAllParametersResponse
import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.GetComposablesCommand
@@ -66,19 +65,31 @@
class ComposeLayoutInspector(
connection: Connection,
+ @Suppress("unused")
private val environment: InspectorEnvironment
) : Inspector(connection) {
/** Cache data which allows us to reuse previously queried inspector nodes */
private class CacheData(
val rootView: View,
+ val trees: List<CacheTree>
+ ) {
+ /** The cached nodes as a map from node id to InspectorNode */
+ val lookup: Map<Long, InspectorNode>
+ get() = _lookup ?: trees.flatMap { it.nodes }
+ .flatMap { it.flatten() }
+ .associateBy { it.id }
+ .also { _lookup = it }
+
+ private var _lookup: Map<Long, InspectorNode>? = null
+ }
+
+ /** Cache data for a tree of [InspectorNode]s under a [viewParent] */
+ internal class CacheTree(
val viewParent: View,
val nodes: List<InspectorNode>,
val viewsToSkip: List<Long>
- ) {
- /** The cached nodes as a map from node id to InspectorNode */
- val lookup = nodes.flatMap { it.flatten() }.associateBy { it.id }
- }
+ )
private val layoutInspectorTree = LayoutInspectorTree()
@@ -142,21 +153,13 @@
val windowPos = IntOffset(location[0], location[1])
val stringTable = StringTable()
- val nodes = data?.nodes ?: emptyList()
- val composeNodes = nodes.toComposableNodes(stringTable, windowPos)
+ val trees = data?.trees ?: emptyList()
+ val roots = trees.map { it.toComposableRoot(stringTable, windowPos) }
callback.reply {
getComposablesResponse = GetComposablesResponse.newBuilder().apply {
addAllStrings(stringTable.toStringEntries())
- addRoots(
- ComposableRoot.newBuilder().apply {
- if (data != null) {
- viewId = data.viewParent.uniqueDrawingId
- addAllNodes(composeNodes)
- addAllViewsToSkip(data.viewsToSkip)
- }
- }
- )
+ addAllRoots(roots)
}.build()
}
}
@@ -284,15 +287,20 @@
val data = ThreadUtils.runOnMainThread {
layoutInspectorTree.resetAccumulativeState()
- val data = getAndroidComposeViews(rootViewId, skipSystemComposables, generation).map {
- CacheData(it.rootView, it.viewParent, it.createNodes(), it.viewsToSkip)
+ val composeViews = getAndroidComposeViews(rootViewId, skipSystemComposables, generation)
+ val composeViewsByRoot = composeViews.groupBy { it.rootView.uniqueDrawingId }
+ val data = composeViewsByRoot.mapValues { (_, composeViews) ->
+ CacheData(
+ composeViews.first().rootView,
+ composeViews.map { CacheTree(it.viewParent, it.createNodes(), it.viewsToSkip) }
+ )
}
layoutInspectorTree.resetAccumulativeState()
data
}.get()
cachedNodes.clear()
- data.associateByTo(cachedNodes) { it.rootView.uniqueDrawingId }
+ cachedNodes.putAll(data)
cachedGeneration = generation
cachedSystemComposablesSkipped = skipSystemComposables
return cachedNodes[rootViewId]
diff --git a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ComposeExtensions.kt b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ComposeExtensions.kt
index fa17f3c..3526f27 100644
--- a/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ComposeExtensions.kt
+++ b/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection/proto/ComposeExtensions.kt
@@ -18,6 +18,7 @@
import android.view.inspector.WindowInspector
import androidx.annotation.VisibleForTesting
+import androidx.compose.ui.inspection.ComposeLayoutInspector.CacheTree
import androidx.compose.ui.inspection.LambdaLocation
import androidx.compose.ui.inspection.inspector.InspectorNode
import androidx.compose.ui.inspection.inspector.NodeParameter
@@ -28,6 +29,7 @@
import androidx.compose.ui.unit.IntOffset
import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.Bounds
import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.ComposableNode
+import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.ComposableRoot
import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.LambdaValue
import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.Parameter
import layoutinspector.compose.inspection.LayoutInspectorComposeProtocol.ParameterReference
@@ -233,12 +235,14 @@
}.build()
}
-fun Iterable<InspectorNode>.toComposableNodes(
+internal fun CacheTree.toComposableRoot(
stringTable: StringTable,
windowPos: IntOffset
-): List<ComposableNode> {
- return this.map { it.toComposableNode(stringTable, windowPos) }
-}
+): ComposableRoot = ComposableRoot.newBuilder().also { root ->
+ root.viewId = viewParent.uniqueDrawingId
+ root.addAllNodes(nodes.map { it.toComposableNode(stringTable, windowPos) })
+ root.addAllViewsToSkip(viewsToSkip)
+}.build()
fun Iterable<NodeParameter>.convertAll(stringTable: StringTable): List<Parameter> {
return this.map { it.convert(stringTable) }
diff --git a/compose/ui/ui-test-junit4/OWNERS b/compose/ui/ui-test-junit4/OWNERS
index 305021a..b2c0d56 100644
--- a/compose/ui/ui-test-junit4/OWNERS
+++ b/compose/ui/ui-test-junit4/OWNERS
@@ -1,2 +1 @@
-pavlis@google.com
jellefresen@google.com
diff --git a/compose/ui/ui-test-junit4/api/1.1.0-beta04.txt b/compose/ui/ui-test-junit4/api/1.1.0-beta04.txt
index a71c843..56b11a6 100644
--- a/compose/ui/ui-test-junit4/api/1.1.0-beta04.txt
+++ b/compose/ui/ui-test-junit4/api/1.1.0-beta04.txt
@@ -76,7 +76,7 @@
package androidx.compose.ui.test.junit4.android {
- public final class ComposeNotIdleException extends java.lang.Throwable {
+ public final class ComposeNotIdleException extends java.lang.Exception {
ctor public ComposeNotIdleException(String? message, Throwable? cause);
}
diff --git a/compose/ui/ui-test-junit4/api/current.ignore b/compose/ui/ui-test-junit4/api/current.ignore
index 6e4d61f..a21b2a6 100644
--- a/compose/ui/ui-test-junit4/api/current.ignore
+++ b/compose/ui/ui-test-junit4/api/current.ignore
@@ -1,7 +1,3 @@
// Baseline format: 1.0
-RemovedClass: androidx.compose.ui.test.junit4.android.ComposeIdlingResource_androidKt:
- Removed class androidx.compose.ui.test.junit4.android.ComposeIdlingResource_androidKt
-RemovedClass: androidx.compose.ui.test.junit4.android.ComposeRootRegistry_androidKt:
- Removed class androidx.compose.ui.test.junit4.android.ComposeRootRegistry_androidKt
-RemovedClass: androidx.compose.ui.test.junit4.android.EspressoLink_androidKt:
- Removed class androidx.compose.ui.test.junit4.android.EspressoLink_androidKt
+ChangedSuperclass: androidx.compose.ui.test.junit4.android.ComposeNotIdleException:
+ Class androidx.compose.ui.test.junit4.android.ComposeNotIdleException superclass changed from java.lang.Throwable to java.lang.Exception
diff --git a/compose/ui/ui-test-junit4/api/current.txt b/compose/ui/ui-test-junit4/api/current.txt
index a71c843..56b11a6 100644
--- a/compose/ui/ui-test-junit4/api/current.txt
+++ b/compose/ui/ui-test-junit4/api/current.txt
@@ -76,7 +76,7 @@
package androidx.compose.ui.test.junit4.android {
- public final class ComposeNotIdleException extends java.lang.Throwable {
+ public final class ComposeNotIdleException extends java.lang.Exception {
ctor public ComposeNotIdleException(String? message, Throwable? cause);
}
diff --git a/compose/ui/ui-test-junit4/api/public_plus_experimental_1.1.0-beta04.txt b/compose/ui/ui-test-junit4/api/public_plus_experimental_1.1.0-beta04.txt
index a71c843..56b11a6 100644
--- a/compose/ui/ui-test-junit4/api/public_plus_experimental_1.1.0-beta04.txt
+++ b/compose/ui/ui-test-junit4/api/public_plus_experimental_1.1.0-beta04.txt
@@ -76,7 +76,7 @@
package androidx.compose.ui.test.junit4.android {
- public final class ComposeNotIdleException extends java.lang.Throwable {
+ public final class ComposeNotIdleException extends java.lang.Exception {
ctor public ComposeNotIdleException(String? message, Throwable? cause);
}
diff --git a/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt b/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
index a71c843..56b11a6 100644
--- a/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui-test-junit4/api/public_plus_experimental_current.txt
@@ -76,7 +76,7 @@
package androidx.compose.ui.test.junit4.android {
- public final class ComposeNotIdleException extends java.lang.Throwable {
+ public final class ComposeNotIdleException extends java.lang.Exception {
ctor public ComposeNotIdleException(String? message, Throwable? cause);
}
diff --git a/compose/ui/ui-test-junit4/api/restricted_1.1.0-beta04.txt b/compose/ui/ui-test-junit4/api/restricted_1.1.0-beta04.txt
index a71c843..56b11a6 100644
--- a/compose/ui/ui-test-junit4/api/restricted_1.1.0-beta04.txt
+++ b/compose/ui/ui-test-junit4/api/restricted_1.1.0-beta04.txt
@@ -76,7 +76,7 @@
package androidx.compose.ui.test.junit4.android {
- public final class ComposeNotIdleException extends java.lang.Throwable {
+ public final class ComposeNotIdleException extends java.lang.Exception {
ctor public ComposeNotIdleException(String? message, Throwable? cause);
}
diff --git a/compose/ui/ui-test-junit4/api/restricted_current.ignore b/compose/ui/ui-test-junit4/api/restricted_current.ignore
index 6e4d61f..a21b2a6 100644
--- a/compose/ui/ui-test-junit4/api/restricted_current.ignore
+++ b/compose/ui/ui-test-junit4/api/restricted_current.ignore
@@ -1,7 +1,3 @@
// Baseline format: 1.0
-RemovedClass: androidx.compose.ui.test.junit4.android.ComposeIdlingResource_androidKt:
- Removed class androidx.compose.ui.test.junit4.android.ComposeIdlingResource_androidKt
-RemovedClass: androidx.compose.ui.test.junit4.android.ComposeRootRegistry_androidKt:
- Removed class androidx.compose.ui.test.junit4.android.ComposeRootRegistry_androidKt
-RemovedClass: androidx.compose.ui.test.junit4.android.EspressoLink_androidKt:
- Removed class androidx.compose.ui.test.junit4.android.EspressoLink_androidKt
+ChangedSuperclass: androidx.compose.ui.test.junit4.android.ComposeNotIdleException:
+ Class androidx.compose.ui.test.junit4.android.ComposeNotIdleException superclass changed from java.lang.Throwable to java.lang.Exception
diff --git a/compose/ui/ui-test-junit4/api/restricted_current.txt b/compose/ui/ui-test-junit4/api/restricted_current.txt
index a71c843..56b11a6 100644
--- a/compose/ui/ui-test-junit4/api/restricted_current.txt
+++ b/compose/ui/ui-test-junit4/api/restricted_current.txt
@@ -76,7 +76,7 @@
package androidx.compose.ui.test.junit4.android {
- public final class ComposeNotIdleException extends java.lang.Throwable {
+ public final class ComposeNotIdleException extends java.lang.Exception {
ctor public ComposeNotIdleException(String? message, Throwable? cause);
}
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeNotIdleException.android.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeNotIdleException.android.kt
index bd68df1..815c8d6 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeNotIdleException.android.kt
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeNotIdleException.android.kt
@@ -19,4 +19,4 @@
/**
* Thrown in cases where Compose can't get idle in Espresso's defined time limit.
*/
-class ComposeNotIdleException(message: String?, cause: Throwable?) : Throwable(message, cause)
+class ComposeNotIdleException(message: String?, cause: Throwable?) : Exception(message, cause)
diff --git a/compose/ui/ui-test-manifest/OWNERS b/compose/ui/ui-test-manifest/OWNERS
index 42abc4e..b2c0d56 100644
--- a/compose/ui/ui-test-manifest/OWNERS
+++ b/compose/ui/ui-test-manifest/OWNERS
@@ -1,2 +1 @@
jellefresen@google.com
-pavlis@google.com
diff --git a/compose/ui/ui-test-manifest/integration-tests/testapp/OWNERS b/compose/ui/ui-test-manifest/integration-tests/testapp/OWNERS
index 42abc4e..b2c0d56 100644
--- a/compose/ui/ui-test-manifest/integration-tests/testapp/OWNERS
+++ b/compose/ui/ui-test-manifest/integration-tests/testapp/OWNERS
@@ -1,2 +1 @@
jellefresen@google.com
-pavlis@google.com
diff --git a/compose/ui/ui-test/OWNERS b/compose/ui/ui-test/OWNERS
index 305021a..b2c0d56 100644
--- a/compose/ui/ui-test/OWNERS
+++ b/compose/ui/ui-test/OWNERS
@@ -1,2 +1 @@
-pavlis@google.com
jellefresen@google.com
diff --git a/compose/ui/ui-text/OWNERS b/compose/ui/ui-text/OWNERS
index f00bb31..9a0b796 100644
--- a/compose/ui/ui-text/OWNERS
+++ b/compose/ui/ui-text/OWNERS
@@ -1,6 +1,5 @@
include /TEXT_OWNERS
-pavlis@google.com
adamp@google.com
mount@google.com
popam@google.com
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TempListUtils.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TempListUtils.kt
index 79b7c59..b86d941 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TempListUtils.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/TempListUtils.kt
@@ -24,6 +24,10 @@
/**
* Returns a list containing only elements matching the given [predicate].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastFilter(predicate: (T) -> Boolean): List<T> {
@@ -40,6 +44,10 @@
* having distinct keys returned by the given [selector] function.
*
* The elements in the resulting list are in the same order as they were in the source collection.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, K> List<T>.fastDistinctBy(selector: (T) -> K): List<T> {
@@ -56,6 +64,10 @@
/**
* Returns the first element yielding the largest value of the given function or `null` if there
* are no elements.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R : Comparable<R>> List<T>.fastMinByOrNull(selector: (T) -> R): T? {
@@ -80,7 +92,12 @@
*
* Returns the specified [initial] value if the collection is empty.
*
- * @param [operation] function that takes current accumulator value and an element, and calculates the next accumulator value.
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
+ *
+ * @param [operation] function that takes current accumulator value and an element, and calculates
+ * the next accumulator value.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R> List<T>.fastFold(initial: R, operation: (acc: R, T) -> R): R {
@@ -95,6 +112,10 @@
/**
* Returns a single list of all elements yielded from results of [transform] function being invoked
* on each element of original collection.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R> List<T>.fastFlatMap(transform: (T) -> Iterable<R>): List<R> {
@@ -109,6 +130,10 @@
/**
* Returns a list containing all elements not matching the given [predicate].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastFilterNot(predicate: (T) -> Boolean): List<T> {
@@ -122,6 +147,10 @@
/**
* Returns a list containing the first elements satisfying the given [predicate].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastTakeWhile(predicate: (T) -> Boolean): List<T> {
@@ -139,6 +168,10 @@
/**
* Returns a list containing all elements except first [n] elements.
*
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
+ *
* @throws IllegalArgumentException if [n] is negative.
*/
internal fun <T> List<T>.fastDrop(n: Int): List<T> {
@@ -167,6 +200,10 @@
* If the collection could be huge, you can specify a non-negative value of [limit], in which case
* only the first [limit] elements will be appended, followed by the [truncated] string (which
* defaults to "...").
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
internal fun <T> List<T>.fastJoinToString(
separator: CharSequence = ", ",
@@ -187,6 +224,10 @@
* If the collection could be huge, you can specify a non-negative value of [limit], in which
* case only the first [limit] elements will be appended, followed by the [truncated] string
* (which defaults to "...").
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
private fun <T, A : Appendable> List<T>.fastJoinTo(
buffer: A,
diff --git a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt
index f52e504..863a04a 100644
--- a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt
+++ b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/ComposeViewAdapterTest.kt
@@ -17,6 +17,7 @@
package androidx.compose.ui.tooling
import android.app.Activity
+import android.os.Build
import android.os.Bundle
import androidx.compose.animation.core.InternalAnimationApi
import androidx.compose.ui.tooling.animation.PreviewAnimationClock
@@ -336,10 +337,14 @@
onDraw = { onDrawCounter++ }
)
}
+
+ // API before 22, might issue an additional draw under testing.
+ val expectedDrawCount = if (Build.VERSION.SDK_INT < 22) 2 else 1
repeat(5) {
activityTestRule.runOnUiThread {
assertEquals(1, compositionCount.get())
- assertTrue("At most, 1 draw is expected", onDrawCounter < 2)
+ assertTrue("At most, $expectedDrawCount draw is expected ($onDrawCounter happened)",
+ onDrawCounter <= expectedDrawCount)
}
Thread.sleep(250)
}
diff --git a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/ListUtils.kt b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/ListUtils.kt
index bf2f53d..bfa9798 100644
--- a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/ListUtils.kt
+++ b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/ListUtils.kt
@@ -22,6 +22,10 @@
/**
* Iterates through a [List] using the index and calls [action] for each item.
* This does not allocate an iterator like [Iterable.forEach].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
inline fun <T> List<T>.fastForEach(action: (T) -> Unit) {
@@ -35,6 +39,10 @@
/**
* Iterates through a [List] using the index and calls [action] for each item.
* This does not allocate an iterator like [Iterable.forEachIndexed].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
inline fun <T> List<T>.fastForEachIndexed(action: (Int, T) -> Unit) {
@@ -47,6 +55,10 @@
/**
* Returns `true` if all elements match the given [predicate].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
inline fun <T> List<T>.fastAll(predicate: (T) -> Boolean): Boolean {
@@ -57,6 +69,10 @@
/**
* Returns `true` if at least one element matches the given [predicate].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
inline fun <T> List<T>.fastAny(predicate: (T) -> Boolean): Boolean {
@@ -67,6 +83,10 @@
/**
* Returns the first value that [predicate] returns `true` for or `null` if nothing matches.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
inline fun <T> List<T>.fastFirstOrNull(predicate: (T) -> Boolean): T? {
@@ -78,6 +98,10 @@
/**
* Returns the sum of all values produced by [selector] function applied to each element in the
* list.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
inline fun <T> List<T>.fastSumBy(selector: (T) -> Int): Int {
@@ -92,6 +116,10 @@
/**
* Returns a list containing the results of applying the given [transform] function
* to each element in the original collection.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
inline fun <T, R> List<T>.fastMap(transform: (T) -> R): List<R> {
@@ -107,6 +135,10 @@
/**
* Returns the first element yielding the largest value of the given function or `null` if there
* are no elements.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
inline fun <T, R : Comparable<R>> List<T>.fastMaxBy(selector: (T) -> R): T? {
@@ -128,6 +160,10 @@
/**
* Applies the given [transform] function to each element of the original collection
* and appends the results to the given [destination].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
inline fun <T, R, C : MutableCollection<in R>> List<T>.fastMapTo(
diff --git a/compose/ui/ui/api/public_plus_experimental_1.1.0-beta04.txt b/compose/ui/ui/api/public_plus_experimental_1.1.0-beta04.txt
index b293eee..eced941 100644
--- a/compose/ui/ui/api/public_plus_experimental_1.1.0-beta04.txt
+++ b/compose/ui/ui/api/public_plus_experimental_1.1.0-beta04.txt
@@ -1607,12 +1607,14 @@
method public int getMove();
method public int getPress();
method public int getRelease();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public int getScroll();
method public int getUnknown();
property public final int Enter;
property public final int Exit;
property public final int Move;
property public final int Press;
property public final int Release;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public final int Scroll;
property public final int Unknown;
}
@@ -1670,7 +1672,7 @@
@androidx.compose.runtime.Immutable public final class PointerInputChange {
ctor public PointerInputChange(long id, long uptimeMillis, long position, boolean pressed, long previousUptimeMillis, long previousPosition, boolean previousPressed, androidx.compose.ui.input.pointer.ConsumedData consumed, optional int type);
- ctor @androidx.compose.ui.ExperimentalComposeUiApi public PointerInputChange(long id, long uptimeMillis, long position, boolean pressed, long previousUptimeMillis, long previousPosition, boolean previousPressed, androidx.compose.ui.input.pointer.ConsumedData consumed, int type, java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public void consume();
method public androidx.compose.ui.input.pointer.PointerInputChange copy(optional long id, optional long currentTime, optional long currentPosition, optional boolean currentPressed, optional long previousTime, optional long previousPosition, optional boolean previousPressed, optional androidx.compose.ui.input.pointer.ConsumedData consumed, optional int type);
method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.input.pointer.PointerInputChange copy(optional long id, optional long currentTime, optional long currentPosition, optional boolean currentPressed, optional long previousTime, optional long previousPosition, optional boolean previousPressed, optional androidx.compose.ui.input.pointer.ConsumedData consumed, optional int type, java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical);
method public androidx.compose.ui.input.pointer.ConsumedData getConsumed();
@@ -1681,16 +1683,20 @@
method public long getPreviousPosition();
method public boolean getPreviousPressed();
method public long getPreviousUptimeMillis();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public long getScrollDelta();
method public int getType();
method public long getUptimeMillis();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public boolean isConsumed();
property public final androidx.compose.ui.input.pointer.ConsumedData consumed;
property @androidx.compose.ui.ExperimentalComposeUiApi public final java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical;
property public final long id;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public final boolean isConsumed;
property public final long position;
property public final boolean pressed;
property public final long previousPosition;
property public final boolean previousPressed;
property public final long previousUptimeMillis;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public final long scrollDelta;
property public final int type;
property public final long uptimeMillis;
}
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index b293eee..eced941 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -1607,12 +1607,14 @@
method public int getMove();
method public int getPress();
method public int getRelease();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public int getScroll();
method public int getUnknown();
property public final int Enter;
property public final int Exit;
property public final int Move;
property public final int Press;
property public final int Release;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public final int Scroll;
property public final int Unknown;
}
@@ -1670,7 +1672,7 @@
@androidx.compose.runtime.Immutable public final class PointerInputChange {
ctor public PointerInputChange(long id, long uptimeMillis, long position, boolean pressed, long previousUptimeMillis, long previousPosition, boolean previousPressed, androidx.compose.ui.input.pointer.ConsumedData consumed, optional int type);
- ctor @androidx.compose.ui.ExperimentalComposeUiApi public PointerInputChange(long id, long uptimeMillis, long position, boolean pressed, long previousUptimeMillis, long previousPosition, boolean previousPressed, androidx.compose.ui.input.pointer.ConsumedData consumed, int type, java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical);
+ method @androidx.compose.ui.ExperimentalComposeUiApi public void consume();
method public androidx.compose.ui.input.pointer.PointerInputChange copy(optional long id, optional long currentTime, optional long currentPosition, optional boolean currentPressed, optional long previousTime, optional long previousPosition, optional boolean previousPressed, optional androidx.compose.ui.input.pointer.ConsumedData consumed, optional int type);
method @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.input.pointer.PointerInputChange copy(optional long id, optional long currentTime, optional long currentPosition, optional boolean currentPressed, optional long previousTime, optional long previousPosition, optional boolean previousPressed, optional androidx.compose.ui.input.pointer.ConsumedData consumed, optional int type, java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical);
method public androidx.compose.ui.input.pointer.ConsumedData getConsumed();
@@ -1681,16 +1683,20 @@
method public long getPreviousPosition();
method public boolean getPreviousPressed();
method public long getPreviousUptimeMillis();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public long getScrollDelta();
method public int getType();
method public long getUptimeMillis();
+ method @androidx.compose.ui.ExperimentalComposeUiApi public boolean isConsumed();
property public final androidx.compose.ui.input.pointer.ConsumedData consumed;
property @androidx.compose.ui.ExperimentalComposeUiApi public final java.util.List<androidx.compose.ui.input.pointer.HistoricalChange> historical;
property public final long id;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public final boolean isConsumed;
property public final long position;
property public final boolean pressed;
property public final long previousPosition;
property public final boolean previousPressed;
property public final long previousUptimeMillis;
+ property @androidx.compose.ui.ExperimentalComposeUiApi public final long scrollDelta;
property public final int type;
property public final long uptimeMillis;
}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/LayoutNodeModifierBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/LayoutNodeModifierBenchmark.kt
index e93fc61..f462daa 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/LayoutNodeModifierBenchmark.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/LayoutNodeModifierBenchmark.kt
@@ -121,6 +121,41 @@
}
}
+ @Test
+ fun setDrawModifiersToSameValue() {
+ modifiers = mutableListOf<Modifier>().apply {
+ repeat(numberOfModifiers) {
+ this += Modifier.drawBehind { }
+ }
+ }
+ combinedModifier = modifiers.fold<Modifier, Modifier>(Modifier) { acc, modifier ->
+ acc.then(modifier)
+ }
+
+ val altModifier = mutableListOf<Modifier>().apply {
+ repeat(numberOfModifiers) {
+ this += Modifier.drawBehind { }
+ }
+ }.fold<Modifier, Modifier>(Modifier) { acc, modifier ->
+ acc.then(modifier)
+ }
+
+ rule.activityTestRule.runOnUiThread {
+ rule.activityTestRule.activity.setContent {
+ TestModifierUpdaterLayout {
+ testModifierUpdater = it
+ }
+ }
+ }
+
+ rule.activityTestRule.runOnUiThread {
+ rule.benchmarkRule.measureRepeated {
+ testModifierUpdater.updateModifier(combinedModifier)
+ testModifierUpdater.updateModifier(altModifier)
+ }
+ }
+ }
+
class SimpleAndroidBenchmarkRule() : TestRule {
@Suppress("DEPRECATION")
val activityTestRule =
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index e1712fe..1e133f1 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -70,7 +70,7 @@
testImplementation(libs.truth)
testImplementation(libs.mockitoCore)
testImplementation(libs.mockitoKotlin)
- testImplementation(libs.robolectric)
+ testImplementation("org.robolectric:robolectric:4.6.1") // TODO(b/205731854): fix tests to work with SDK 31 and robolectric 4.7
testImplementation(project(":compose:ui:ui-test-junit4"))
testImplementation(project(":compose:test-utils"))
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/EventTypesDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/EventTypesDemo.kt
index 36b1565..29478d5 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/EventTypesDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/gestures/EventTypesDemo.kt
@@ -22,7 +22,6 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.Text
@@ -30,8 +29,10 @@
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.input.pointer.PointerEventType
@@ -50,10 +51,10 @@
}
@Composable
-private fun DrawEvents(events: List<PointerEventType>, counts: List<Int>) {
+@OptIn(ExperimentalComposeUiApi::class)
+private fun DrawEvents(events: List<Pair<PointerEventType, Any>>) {
for (i in events.lastIndex downTo 0) {
- val type = events[i]
- val count = counts[i]
+ val (type, value) = events[i]
val color = when (type) {
PointerEventType.Press -> Color.Red
@@ -61,9 +62,10 @@
PointerEventType.Release -> Color.Yellow
PointerEventType.Enter -> Color.Green
PointerEventType.Exit -> Color.Blue
- else -> Color(0xFF800080) // Purple
+ PointerEventType.Scroll -> Color(0xFF800080) // Purple
+ else -> Color.Black
}
- TextItem("$type $count", color)
+ TextItem("$type $value", color)
}
}
@@ -72,59 +74,62 @@
*/
@Composable
fun EventTypesDemo() {
- val innerPointerEventTypes = remember { mutableStateListOf<PointerEventType>() }
- val innerPointerEventCounts = remember { mutableStateListOf<Int>() }
- val outerPointerEventTypes = remember { mutableStateListOf<PointerEventType>() }
- val outerPointerEventCounts = remember { mutableStateListOf<Int>() }
+ val innerPointerEvents = remember { mutableStateListOf<Pair<PointerEventType, Any>>() }
+ val outerPointerEvents = remember { mutableStateListOf<Pair<PointerEventType, Any>>() }
Box(
Modifier.pointerInput(Unit) {
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent()
event.changes.forEach { it.consumeAllChanges() }
- addEvent(event, outerPointerEventTypes, outerPointerEventCounts)
+ addEvent(event, outerPointerEvents)
}
}
}
) {
Column {
- DrawEvents(outerPointerEventTypes, outerPointerEventCounts)
+ DrawEvents(outerPointerEvents)
}
Column(
- Modifier
- .align(Alignment.CenterEnd)
- .requiredSize(200.dp)
+ Modifier.size(200.dp)
.border(2.dp, Color.Black)
+ .align(Alignment.CenterEnd)
.clipToBounds()
.pointerInput(Unit) {
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent()
- addEvent(event, innerPointerEventTypes, innerPointerEventCounts)
+ addEvent(event, innerPointerEvents)
}
}
}
) {
- DrawEvents(innerPointerEventTypes, innerPointerEventCounts)
+ DrawEvents(innerPointerEvents)
}
}
}
+@OptIn(ExperimentalComposeUiApi::class)
private fun addEvent(
event: PointerEvent,
- events: MutableList<PointerEventType>,
- counts: MutableList<Int>
+ events: MutableList<Pair<PointerEventType, Any>>,
) {
event.changes.forEach { it.consumeAllChanges() }
- if (events.lastOrNull() == event.type) {
- counts[counts.lastIndex]++
+ val scrollTotal = event.changes.foldRight(Offset.Zero) { c, acc -> acc + c.scrollDelta }
+ if (events.lastOrNull()?.first == event.type) {
+ val (type, value) = events.last()
+ if (type == PointerEventType.Scroll) {
+ events[events.lastIndex] = type to ((value as Offset) + scrollTotal)
+ } else {
+ events[events.lastIndex] = type to ((value as Int) + 1)
+ }
+ } else if (event.type == PointerEventType.Scroll) {
+ events += event.type to scrollTotal
} else {
- events += event.type
- counts += 1
+ events += event.type to 1
}
while (events.size > 100) {
events.removeAt(0)
- counts.removeAt(0)
}
}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index 4b86181..211a4dc 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -2216,7 +2216,7 @@
dialogComposeView = LocalView.current as AndroidComposeView
delegate = ViewCompat.getAccessibilityDelegate(dialogComposeView!!) as
AndroidComposeViewAccessibilityDelegateCompat
- provider = delegate.getAccessibilityNodeProvider(dialogComposeView).provider
+ provider = delegate.getAccessibilityNodeProvider(dialogComposeView!!).provider
as AccessibilityNodeProvider
with(LocalDensity.current) {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index c1b7f74..2c6747b 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -38,6 +38,7 @@
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.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Stable
@@ -49,6 +50,7 @@
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.testutils.assertPixels
import androidx.compose.ui.draw.DrawModifier
+import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
@@ -3575,6 +3577,76 @@
assertTrue(composeView.isTransitionGroup)
}
+ @Test
+ fun drawnInCorrectLayer() {
+ var innerDrawLatch = CountDownLatch(1)
+ var outerDrawLatch = CountDownLatch(1)
+ var outerColor by mutableStateOf(Color.Blue)
+ var innerColor by mutableStateOf(Color.White)
+ activityTestRule.runOnUiThread {
+ activity.setContent {
+ with(LocalDensity.current) {
+ Box(Modifier.size(30.toDp())
+ .drawBehind {
+ drawRect(outerColor)
+ outerDrawLatch.countDown()
+ }
+ .drawLatchModifier()
+ .padding(10.toDp())
+ .clipToBounds()
+ .drawBehind {
+ // clipped by the layer
+ drawRect(innerColor, Offset(-10f, -10f), Size(30f, 30f))
+ innerDrawLatch.countDown()
+ }
+ .drawLatchModifier()
+ .size(10.toDp())
+ )
+ }
+ }
+ }
+ assertTrue(innerDrawLatch.await(1, TimeUnit.SECONDS))
+ assertTrue(outerDrawLatch.await(1, TimeUnit.SECONDS))
+
+ validateSquareColors(
+ outerColor = Color.Blue,
+ innerColor = Color.White,
+ size = 10
+ )
+
+ innerDrawLatch = CountDownLatch(1)
+ outerDrawLatch = CountDownLatch(1)
+ drawLatch = CountDownLatch(1)
+
+ // changing the inner color should only affect the inner layer
+ innerColor = Color.Yellow
+
+ assertTrue(innerDrawLatch.await(1, TimeUnit.SECONDS))
+
+ validateSquareColors(
+ outerColor = Color.Blue,
+ innerColor = Color.Yellow,
+ size = 10
+ )
+
+ assertEquals(1, outerDrawLatch.count)
+ innerDrawLatch = CountDownLatch(1)
+ drawLatch = CountDownLatch(1)
+
+ // changing the outer color should only affect the outer layer
+ outerColor = Color.Red
+
+ assertTrue(outerDrawLatch.await(1, TimeUnit.SECONDS))
+
+ validateSquareColors(
+ outerColor = Color.Red,
+ innerColor = Color.Yellow,
+ size = 10
+ )
+
+ assertEquals(1, innerDrawLatch.count)
+ }
+
private fun Modifier.layout(onLayout: () -> Unit) = layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CaptureFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CaptureFocusTest.kt
index 0dcf0c8..5e49f81 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CaptureFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/CaptureFocusTest.kt
@@ -44,10 +44,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Active))
+ .focusTarget(FocusModifier(Active))
)
}
@@ -71,10 +71,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(ActiveParent))
+ .focusTarget(FocusModifier(ActiveParent))
)
}
@@ -98,10 +98,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Captured))
+ .focusTarget(FocusModifier(Captured))
)
}
@@ -124,7 +124,7 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
.focusProperties { canFocus = false }
@@ -154,15 +154,16 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
.focusProperties { canFocus = false }
.focusable()
) {
- Box(modifier = Modifier
- .focusRequester(initialFocus)
- .focusable()
+ Box(
+ Modifier
+ .focusRequester(initialFocus)
+ .focusable()
)
}
}
@@ -189,10 +190,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ClearFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ClearFocusTest.kt
index c556531..250c1d7 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ClearFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ClearFocusTest.kt
@@ -50,7 +50,7 @@
// Arrange.
val modifier = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = modifier)
+ Box(Modifier.focusTarget(modifier))
}
// Act.
@@ -71,8 +71,8 @@
val parent = FocusModifier(ActiveParent)
val modifier = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = parent) {
- Box(modifier = modifier)
+ Box(Modifier.focusTarget(parent)) {
+ Box(Modifier.focusTarget(modifier))
}
SideEffect {
parent.focusedChild = modifier.focusNode
@@ -96,7 +96,7 @@
// Arrange.
val modifier = FocusModifier(ActiveParent)
rule.setFocusableContent {
- Box(modifier = modifier)
+ Box(Modifier.focusTarget(modifier))
}
// Act.
@@ -112,9 +112,9 @@
val modifier = FocusModifier(ActiveParent)
val child = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = parent) {
- Box(modifier = modifier) {
- Box(modifier = child)
+ Box(Modifier.focusTarget(parent)) {
+ Box(Modifier.focusTarget(modifier)) {
+ Box(Modifier.focusTarget(child))
}
}
SideEffect {
@@ -144,10 +144,10 @@
val grandchild = FocusModifier(ActiveParent)
val greatGrandchild = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = modifier) {
- Box(modifier = child) {
- Box(modifier = grandchild) {
- Box(modifier = greatGrandchild)
+ Box(Modifier.focusTarget(modifier)) {
+ Box(Modifier.focusTarget(child)) {
+ Box(Modifier.focusTarget(grandchild)) {
+ Box(Modifier.focusTarget(greatGrandchild))
}
}
}
@@ -181,7 +181,7 @@
// Arrange.
val modifier = FocusModifier(Captured)
rule.setFocusableContent {
- Box(modifier = modifier)
+ Box(Modifier.focusTarget(modifier))
}
// Act.
@@ -210,8 +210,8 @@
val parent = FocusModifier(ActiveParent)
val modifier = FocusModifier(Captured)
rule.setFocusableContent {
- Box(modifier = parent) {
- Box(modifier = modifier)
+ Box(Modifier.focusTarget(parent)) {
+ Box(Modifier.focusTarget(modifier))
}
SideEffect {
parent.focusedChild = modifier.focusNode
@@ -243,7 +243,7 @@
// Arrange.
val modifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = modifier)
+ Box(Modifier.focusTarget(modifier))
}
// Act.
@@ -263,7 +263,11 @@
// Arrange.
val modifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = Modifier.focusProperties { canFocus = false }.then(modifier))
+ Box(
+ Modifier
+ .focusProperties { canFocus = false }
+ .focusTarget(modifier)
+ )
}
// Act.
@@ -283,7 +287,7 @@
// Arrange.
val modifier = FocusModifier(DeactivatedParent)
rule.setFocusableContent {
- Box(modifier = modifier)
+ Box(Modifier.focusTarget(modifier))
}
// Act.
@@ -299,9 +303,13 @@
val modifier = FocusModifier(ActiveParent)
val child = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = parent) {
- Box(modifier = Modifier.focusProperties { canFocus = false }.then(modifier)) {
- Box(modifier = child)
+ Box(Modifier.focusTarget(parent)) {
+ Box(
+ Modifier
+ .focusProperties { canFocus = false }
+ .focusTarget(modifier)
+ ) {
+ Box(Modifier.focusTarget(child))
}
}
SideEffect {
@@ -330,9 +338,15 @@
val modifier = FocusModifier(ActiveParent)
val child = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = Modifier.focusProperties { canFocus = false }.then(parent)) {
- Box(modifier = Modifier.focusProperties { canFocus = false }.then(modifier)) {
- Box(modifier = child)
+ Box(Modifier
+ .focusProperties { canFocus = false }
+ .focusTarget(parent)
+ ) {
+ Box(Modifier
+ .focusProperties { canFocus = false }
+ .focusTarget(modifier)
+ ) {
+ Box(Modifier.focusTarget(child))
}
}
SideEffect {
@@ -363,14 +377,17 @@
val greatGrandchild = FocusModifier(ActiveParent)
val greatGreatGrandchild = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = Modifier.focusProperties { canFocus = false }.then(modifier)) {
- Box(modifier = child) {
- Box(modifier = Modifier
+ Box(Modifier
+ .focusProperties { canFocus = false }
+ .focusTarget(modifier)
+ ) {
+ Box(modifier = Modifier.focusTarget(child)) {
+ Box(Modifier
.focusProperties { canFocus = false }
- .then(grandchild)
+ .focusTarget(grandchild)
) {
- Box(modifier = greatGrandchild) {
- Box(modifier = greatGreatGrandchild)
+ Box(Modifier.focusTarget(greatGrandchild)) {
+ Box(Modifier.focusTarget(greatGreatGrandchild))
}
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindFocusableChildrenTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindFocusableChildrenTest.kt
index f3038a1..8982445 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindFocusableChildrenTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindFocusableChildrenTest.kt
@@ -51,12 +51,12 @@
// layoutNode--focusNode1--focusNode2--focusNode3--focusNode4
rule.setContent {
Box(
- modifier = Modifier
- .then(focusModifier1)
- .focusProperties { canFocus = false }
- .then(focusModifier2)
- .then(focusModifier3)
- .then(focusModifier4)
+ Modifier
+ .focusTarget(focusModifier1)
+ .focusProperties { canFocus = false }
+ .focusTarget(focusModifier2)
+ .focusTarget(focusModifier3)
+ .focusTarget(focusModifier4)
)
}
@@ -84,12 +84,12 @@
// layoutNode--focusNode1--nonFocusNode--focusNode2--focusNode3
rule.setContent {
Box(
- modifier = Modifier
- .then(focusModifier1)
+ Modifier
+ .focusTarget(focusModifier1)
.background(color = Red)
.focusProperties { canFocus = false }
- .then(focusModifier2)
- .then(focusModifier3)
+ .focusTarget(focusModifier2)
+ .focusTarget(focusModifier3)
)
}
@@ -121,16 +121,18 @@
val focusModifier3 = FocusModifier(Inactive)
val focusModifier4 = FocusModifier(Inactive)
rule.setContent {
- Box(modifier = parentFocusModifier) {
- Box(modifier = Modifier
- .focusProperties { canFocus = false }
- .then(focusModifier1)
- .then(focusModifier2)
+ Box(Modifier.focusTarget(parentFocusModifier)) {
+ Box(
+ Modifier
+ .focusProperties { canFocus = false }
+ .focusTarget(focusModifier1)
+ .focusTarget(focusModifier2)
)
- Box(modifier = Modifier
- .then(focusModifier3)
- .focusProperties { canFocus = false }
- .then(focusModifier4)
+ Box(
+ Modifier
+ .focusTarget(focusModifier3)
+ .focusProperties { canFocus = false }
+ .focusTarget(focusModifier4)
)
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindParentFocusNodeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindParentFocusNodeTest.kt
index 494246c..286bd23 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindParentFocusNodeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FindParentFocusNodeTest.kt
@@ -46,7 +46,7 @@
// Arrange.
val focusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(focusModifier))
}
// Act.
@@ -70,12 +70,14 @@
val modifier4 = FocusModifier(Inactive)
val modifier5 = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = modifier1
- .focusProperties { canFocus = !deactivated }
- .then(modifier2)
- .then(modifier3)
- .then(modifier4)
- .then(modifier5)
+ Box(
+ Modifier
+ .focusTarget(modifier1)
+ .focusProperties { canFocus = !deactivated }
+ .focusTarget(modifier2)
+ .focusTarget(modifier3)
+ .focusTarget(modifier4)
+ .focusTarget(modifier5)
)
}
@@ -99,11 +101,12 @@
val modifier3 = FocusModifier(Inactive)
rule.setFocusableContent {
Box(
- modifier = modifier1
+ Modifier
+ .focusTarget(modifier1)
.focusProperties { canFocus = !deactivated }
- .then(modifier2)
+ .focusTarget(modifier2)
.background(color = Red)
- .then(modifier3)
+ .focusTarget(modifier3)
)
}
@@ -128,11 +131,13 @@
val parentFocusModifier2 = FocusModifier(Inactive)
val focusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = parentFocusModifier1
- .focusProperties { canFocus = !deactivated }
- .then(parentFocusModifier2)
+ Box(
+ Modifier
+ .focusTarget(parentFocusModifier1)
+ .focusProperties { canFocus = !deactivated }
+ .focusTarget(parentFocusModifier2)
) {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(focusModifier))
}
}
@@ -162,13 +167,13 @@
val parentFocusModifier = FocusModifier(Inactive)
val focusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = greatGrandparentFocusModifier) {
- Box(modifier = grandparentFocusModifier) {
- Box(modifier = Modifier
+ Box(Modifier.focusTarget(greatGrandparentFocusModifier)) {
+ Box(Modifier.focusTarget(grandparentFocusModifier)) {
+ Box(Modifier
.focusProperties { canFocus = !deactivated }
- .then(parentFocusModifier)
+ .focusTarget(parentFocusModifier)
) {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(focusModifier))
}
}
}
@@ -197,13 +202,13 @@
val grandparentFocusModifier = FocusModifier(Inactive)
val focusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = greatGrandparentFocusModifier) {
- Box(modifier = Modifier
+ Box(Modifier.focusTarget(greatGrandparentFocusModifier)) {
+ Box(Modifier
.focusProperties { canFocus = !deactivated }
- .then(grandparentFocusModifier)
+ .focusTarget(grandparentFocusModifier)
) {
Box {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(focusModifier))
}
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusChangedTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusChangedTest.kt
index ec729ec..88b4d3c 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusChangedTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FocusChangedTest.kt
@@ -46,7 +46,7 @@
modifier = Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Active))
+ .focusTarget(FocusModifier(Active))
)
}
@@ -106,7 +106,7 @@
modifier = Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Captured))
+ .focusTarget(FocusModifier(Captured))
)
}
@@ -196,7 +196,7 @@
modifier = Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
}
@@ -236,7 +236,7 @@
.onFocusChanged { focusState5 = it }
.onFocusChanged { focusState6 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
}
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FreeFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FreeFocusTest.kt
index a0845c9..0ecc2d73a 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FreeFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/FreeFocusTest.kt
@@ -43,10 +43,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Active))
+ .focusTarget(FocusModifier(Active))
)
}
@@ -67,10 +67,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(ActiveParent))
+ .focusTarget(FocusModifier(ActiveParent))
)
}
@@ -91,10 +91,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Captured))
+ .focusTarget(FocusModifier(Captured))
)
}
@@ -115,11 +115,11 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
.focusProperties { canFocus = false }
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
}
@@ -140,10 +140,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/RequestFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/RequestFocusTest.kt
index ad2151b..1e38ac4 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/RequestFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/RequestFocusTest.kt
@@ -25,36 +25,30 @@
import androidx.compose.ui.focus.FocusStateImpl.DeactivatedParent
import androidx.compose.ui.focus.FocusStateImpl.Inactive
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
@SmallTest
-@RunWith(Parameterized::class)
-class RequestFocusTest(private val propagateFocus: Boolean) {
+@RunWith(AndroidJUnit4::class)
+class RequestFocusTest {
@get:Rule
val rule = createComposeRule()
- companion object {
- @JvmStatic
- @Parameterized.Parameters(name = "propagateFocus = {0}")
- fun initParameters() = listOf(true, false)
- }
-
@Test
fun active_isUnchanged() {
// Arrange.
val focusModifier = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(focusModifier))
}
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
@@ -69,12 +63,12 @@
// Arrange.
val focusModifier = FocusModifier(Captured)
rule.setFocusableContent {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(focusModifier))
}
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
@@ -88,12 +82,15 @@
// Arrange.
val focusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = Modifier.focusProperties { canFocus = false }.then(focusModifier))
+ Box(Modifier
+ .focusProperties { canFocus = false }
+ .focusTarget(focusModifier)
+ )
}
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
@@ -107,12 +104,12 @@
// Arrange.
val focusModifier = FocusModifier(ActiveParent)
rule.setFocusableContent {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(focusModifier))
}
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
}
@@ -122,8 +119,8 @@
val focusModifier = FocusModifier(ActiveParent)
val childFocusModifier = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = focusModifier) {
- Box(modifier = childFocusModifier)
+ Box(Modifier.focusTarget(focusModifier)) {
+ Box(Modifier.focusTarget(childFocusModifier))
}
}
rule.runOnIdle {
@@ -132,23 +129,14 @@
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
rule.runOnIdle {
- when (propagateFocus) {
- true -> {
- // Unchanged.
- assertThat(focusModifier.focusState).isEqualTo(ActiveParent)
- assertThat(childFocusModifier.focusState).isEqualTo(Active)
- }
- false -> {
- assertThat(focusModifier.focusState).isEqualTo(Active)
- assertThat(focusModifier.focusedChild).isNull()
- assertThat(childFocusModifier.focusState).isEqualTo(Inactive)
- }
- }
+ assertThat(focusModifier.focusState).isEqualTo(Active)
+ assertThat(focusModifier.focusedChild).isNull()
+ assertThat(childFocusModifier.focusState).isEqualTo(Inactive)
}
}
@@ -157,12 +145,12 @@
// Arrange.
val focusModifier = FocusModifier(DeactivatedParent)
rule.setFocusableContent {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(focusModifier))
}
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
}
@@ -172,8 +160,11 @@
val focusModifier = FocusModifier(ActiveParent)
val childFocusModifier = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = Modifier.focusProperties { canFocus = false }.then(focusModifier)) {
- Box(modifier = childFocusModifier)
+ Box(Modifier
+ .focusProperties { canFocus = false }
+ .focusTarget(focusModifier)
+ ) {
+ Box(Modifier.focusTarget(childFocusModifier))
}
}
rule.runOnIdle {
@@ -182,14 +173,14 @@
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
rule.runOnIdle {
- // Unchanged.
- assertThat(focusModifier.focusState).isEqualTo(DeactivatedParent)
- assertThat(childFocusModifier.focusState).isEqualTo(Active)
+ // Unchanged.
+ assertThat(focusModifier.focusState).isEqualTo(DeactivatedParent)
+ assertThat(childFocusModifier.focusState).isEqualTo(Active)
}
}
@@ -200,9 +191,12 @@
val childFocusModifier = FocusModifier(Active)
val grandchildFocusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = Modifier.focusProperties { canFocus = false }.then(focusModifier)) {
- Box(modifier = childFocusModifier) {
- Box(modifier = grandchildFocusModifier)
+ Box(Modifier
+ .focusProperties { canFocus = false }
+ .focusTarget(focusModifier)
+ ) {
+ Box(Modifier.focusTarget(childFocusModifier)) {
+ Box(Modifier.focusTarget(grandchildFocusModifier))
}
}
}
@@ -212,24 +206,15 @@
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
rule.runOnIdle {
- when (propagateFocus) {
- true -> {
- assertThat(focusModifier.focusState).isEqualTo(DeactivatedParent)
- assertThat(childFocusModifier.focusState).isEqualTo(Active)
- assertThat(grandchildFocusModifier.focusState).isEqualTo(Inactive)
- }
- false -> {
assertThat(focusModifier.focusState).isEqualTo(DeactivatedParent)
assertThat(childFocusModifier.focusState).isEqualTo(Active)
assertThat(childFocusModifier.focusedChild).isNull()
assertThat(grandchildFocusModifier.focusState).isEqualTo(Inactive)
- }
- }
}
}
@@ -238,12 +223,12 @@
// Arrange.
val rootFocusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = rootFocusModifier)
+ Box(Modifier.focusTarget(rootFocusModifier))
}
// Act.
rule.runOnIdle {
- rootFocusModifier.focusNode.requestFocus(propagateFocus)
+ rootFocusModifier.focusNode.requestFocus()
}
// Assert.
@@ -258,29 +243,20 @@
val rootFocusModifier = FocusModifier(Inactive)
val childFocusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = rootFocusModifier) {
- Box(modifier = childFocusModifier)
+ Box(Modifier.focusTarget(rootFocusModifier)) {
+ Box(Modifier.focusTarget(childFocusModifier))
}
}
// Act.
rule.runOnIdle {
- rootFocusModifier.focusNode.requestFocus(propagateFocus)
+ rootFocusModifier.focusNode.requestFocus()
}
// Assert.
rule.runOnIdle {
- when (propagateFocus) {
- true -> {
- // Unchanged.
- assertThat(rootFocusModifier.focusState).isEqualTo(ActiveParent)
- assertThat(childFocusModifier.focusState).isEqualTo(Active)
- }
- false -> {
assertThat(rootFocusModifier.focusState).isEqualTo(Active)
assertThat(childFocusModifier.focusState).isEqualTo(Inactive)
- }
- }
}
}
@@ -291,32 +267,23 @@
val focusModifier = FocusModifier(Inactive)
val childFocusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = parentFocusModifier) {
- Box(modifier = focusModifier) {
- Box(modifier = childFocusModifier)
+ Box(Modifier.focusTarget(parentFocusModifier)) {
+ Box(Modifier.focusTarget(focusModifier)) {
+ Box(Modifier.focusTarget(childFocusModifier))
}
}
}
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
rule.runOnIdle {
- when (propagateFocus) {
- true -> {
- assertThat(parentFocusModifier.focusState).isEqualTo(ActiveParent)
- assertThat(focusModifier.focusState).isEqualTo(ActiveParent)
- assertThat(childFocusModifier.focusState).isEqualTo(Active)
- }
- false -> {
- assertThat(parentFocusModifier.focusState).isEqualTo(ActiveParent)
+ assertThat(parentFocusModifier.focusState).isEqualTo(ActiveParent)
assertThat(focusModifier.focusState).isEqualTo(Active)
assertThat(childFocusModifier.focusState).isEqualTo(Inactive)
- }
- }
}
}
@@ -325,12 +292,12 @@
// Arrange.
val rootFocusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = rootFocusModifier)
+ Box(Modifier.focusTarget(rootFocusModifier))
}
// Act.
rule.runOnIdle {
- rootFocusModifier.focusNode.requestFocus(propagateFocus)
+ rootFocusModifier.focusNode.requestFocus()
}
// Assert.
@@ -345,22 +312,19 @@
val rootFocusModifier = FocusModifier(Inactive)
val childFocusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = rootFocusModifier) {
- Box(modifier = childFocusModifier)
+ Box(Modifier.focusTarget(rootFocusModifier)) {
+ Box(Modifier.focusTarget(childFocusModifier))
}
}
// Act.
rule.runOnIdle {
- rootFocusModifier.focusNode.requestFocus(propagateFocus)
+ rootFocusModifier.focusNode.requestFocus()
}
// Assert.
rule.runOnIdle {
- when (propagateFocus) {
- true -> assertThat(rootFocusModifier.focusState).isEqualTo(ActiveParent)
- false -> assertThat(rootFocusModifier.focusState).isEqualTo(Active)
- }
+ assertThat(rootFocusModifier.focusState).isEqualTo(Active)
}
}
@@ -371,24 +335,21 @@
val parentFocusModifier = FocusModifier(Inactive)
val childFocusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = grandParentFocusModifier) {
- Box(modifier = parentFocusModifier) {
- Box(modifier = childFocusModifier)
+ Box(Modifier.focusTarget(grandParentFocusModifier)) {
+ Box(Modifier.focusTarget(parentFocusModifier)) {
+ Box(Modifier.focusTarget(childFocusModifier))
}
}
}
// Act.
rule.runOnIdle {
- parentFocusModifier.focusNode.requestFocus(propagateFocus)
+ parentFocusModifier.focusNode.requestFocus()
}
// Assert.
rule.runOnIdle {
- when (propagateFocus) {
- true -> assertThat(parentFocusModifier.focusState).isEqualTo(ActiveParent)
- false -> assertThat(parentFocusModifier.focusState).isEqualTo(Active)
- }
+ assertThat(parentFocusModifier.focusState).isEqualTo(Active)
}
}
@@ -399,16 +360,16 @@
val parentFocusModifier = FocusModifier(Inactive)
val childFocusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = grandParentFocusModifier) {
- Box(modifier = parentFocusModifier) {
- Box(modifier = childFocusModifier)
+ Box(Modifier.focusTarget(grandParentFocusModifier)) {
+ Box(Modifier.focusTarget(parentFocusModifier)) {
+ Box(Modifier.focusTarget(childFocusModifier))
}
}
}
// Act.
rule.runOnIdle {
- childFocusModifier.focusNode.requestFocus(propagateFocus)
+ childFocusModifier.focusNode.requestFocus()
}
// Assert.
rule.runOnIdle {
@@ -423,16 +384,16 @@
val parentFocusModifier = FocusModifier(Inactive)
val childFocusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = grandParentFocusModifier) {
- Box(modifier = parentFocusModifier) {
- Box(modifier = childFocusModifier)
+ Box(Modifier.focusTarget(grandParentFocusModifier)) {
+ Box(Modifier.focusTarget(parentFocusModifier)) {
+ Box(Modifier.focusTarget(childFocusModifier))
}
}
}
// Act.
rule.runOnIdle {
- childFocusModifier.focusNode.requestFocus(propagateFocus)
+ childFocusModifier.focusNode.requestFocus()
}
// Assert.
@@ -447,14 +408,14 @@
val parentFocusModifier = FocusModifier(Active)
val focusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = parentFocusModifier) {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(parentFocusModifier)) {
+ Box(Modifier.focusTarget(focusModifier))
}
}
// After executing requestFocus, siblingNode will be 'Active'.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
@@ -470,8 +431,8 @@
val parentFocusModifier = FocusModifier(ActiveParent)
val focusModifier = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = parentFocusModifier) {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(parentFocusModifier)) {
+ Box(Modifier.focusTarget(focusModifier))
}
}
rule.runOnIdle {
@@ -480,21 +441,13 @@
// Act.
rule.runOnIdle {
- parentFocusModifier.focusNode.requestFocus(propagateFocus)
+ parentFocusModifier.focusNode.requestFocus()
}
// Assert.
rule.runOnIdle {
- when (propagateFocus) {
- true -> {
- assertThat(parentFocusModifier.focusState).isEqualTo(ActiveParent)
- assertThat(focusModifier.focusState).isEqualTo(Active)
- }
- false -> {
- assertThat(parentFocusModifier.focusState).isEqualTo(Active)
+ assertThat(parentFocusModifier.focusState).isEqualTo(Active)
assertThat(focusModifier.focusState).isEqualTo(Inactive)
- }
- }
}
}
@@ -504,8 +457,8 @@
val parentFocusModifier = FocusModifier(ActiveParent)
val focusModifier = FocusModifier(Captured)
rule.setFocusableContent {
- Box(modifier = parentFocusModifier) {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(parentFocusModifier)) {
+ Box(Modifier.focusTarget(focusModifier))
}
}
rule.runOnIdle {
@@ -514,7 +467,7 @@
// Act.
rule.runOnIdle {
- parentFocusModifier.focusNode.requestFocus(propagateFocus)
+ parentFocusModifier.focusNode.requestFocus()
}
// Assert.
@@ -531,9 +484,9 @@
val focusModifier = FocusModifier(Inactive)
val siblingModifier = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = parentFocusModifier) {
- Box(modifier = focusModifier)
- Box(modifier = siblingModifier)
+ Box(Modifier.focusTarget(parentFocusModifier)) {
+ Box(Modifier.focusTarget(focusModifier))
+ Box(Modifier.focusTarget(siblingModifier))
}
}
rule.runOnIdle {
@@ -542,7 +495,7 @@
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
@@ -560,9 +513,9 @@
val focusModifier = FocusModifier(Inactive)
val siblingModifier = FocusModifier(Captured)
rule.setFocusableContent {
- Box(modifier = parentFocusModifier) {
- Box(modifier = focusModifier)
- Box(modifier = siblingModifier)
+ Box(Modifier.focusTarget(parentFocusModifier)) {
+ Box(Modifier.focusTarget(focusModifier))
+ Box(Modifier.focusTarget(siblingModifier))
}
}
rule.runOnIdle {
@@ -571,7 +524,7 @@
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
@@ -591,12 +544,12 @@
val auntModifier = FocusModifier(ActiveParent)
val cousinModifier = FocusModifier(Active)
rule.setFocusableContent {
- Box(modifier = grandParentModifier) {
- Box(modifier = parentModifier) {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(grandParentModifier)) {
+ Box(Modifier.focusTarget(parentModifier)) {
+ Box(Modifier.focusTarget(focusModifier))
}
- Box(modifier = auntModifier) {
- Box(modifier = cousinModifier)
+ Box(Modifier.focusTarget(auntModifier)) {
+ Box(Modifier.focusTarget(cousinModifier))
}
}
}
@@ -613,7 +566,7 @@
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
@@ -630,16 +583,16 @@
val parentModifier = FocusModifier(Inactive)
val focusModifier = FocusModifier(Inactive)
rule.setFocusableContent {
- Box(modifier = grandParentModifier) {
- Box(modifier = parentModifier) {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(grandParentModifier)) {
+ Box(Modifier.focusTarget(parentModifier)) {
+ Box(Modifier.focusTarget(focusModifier))
}
}
}
// Act.
rule.runOnIdle {
- focusModifier.focusNode.requestFocus(propagateFocus)
+ focusModifier.focusNode.requestFocus()
}
// Assert.
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterCaptureFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterCaptureFocusTest.kt
index 749d6fa..84ee29a 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterCaptureFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterCaptureFocusTest.kt
@@ -42,10 +42,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Active))
+ .focusTarget(FocusModifier(Active))
)
}
@@ -66,10 +66,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Captured))
+ .focusTarget(FocusModifier(Captured))
)
}
@@ -90,10 +90,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
}
@@ -115,16 +115,16 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState1 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState2 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Active))
+ .focusTarget(FocusModifier(Active))
)
}
@@ -147,16 +147,16 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState1 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState2 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Captured))
+ .focusTarget(FocusModifier(Captured))
)
}
@@ -179,16 +179,16 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState1 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState2 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterFreeFocusTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterFreeFocusTest.kt
index 4848951..9805036 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterFreeFocusTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/focus/ReusedFocusRequesterFreeFocusTest.kt
@@ -42,10 +42,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Active))
+ .focusTarget(FocusModifier(Active))
)
}
@@ -66,10 +66,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Captured))
+ .focusTarget(FocusModifier(Captured))
)
}
@@ -90,10 +90,10 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
}
@@ -115,16 +115,16 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState1 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState2 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Active))
+ .focusTarget(FocusModifier(Active))
)
}
@@ -147,16 +147,16 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState1 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState2 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Captured))
+ .focusTarget(FocusModifier(Captured))
)
}
@@ -179,16 +179,16 @@
val focusRequester = FocusRequester()
rule.setFocusableContent {
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState1 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
Box(
- modifier = Modifier
+ Modifier
.onFocusChanged { focusState2 = it }
.focusRequester(focusRequester)
- .then(FocusModifier(Inactive))
+ .focusTarget(FocusModifier(Inactive))
)
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/Utils.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/Utils.kt
index 31dc186..fb57437 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/Utils.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/gesture/Utils.kt
@@ -17,6 +17,8 @@
package androidx.compose.ui.gesture
import android.view.MotionEvent
+import android.view.MotionEvent.AXIS_HSCROLL
+import android.view.MotionEvent.AXIS_VSCROLL
import android.view.View
// We only need this because IR compiler doesn't like converting lambdas to Runnables
@@ -87,8 +89,10 @@
MotionEvent.PointerProperties().apply { this.id = id }
@Suppress("RemoveRedundantQualifierName")
-internal fun PointerCoords(x: Float, y: Float) =
+internal fun PointerCoords(x: Float, y: Float, scrollX: Float = 0f, scrollY: Float = 0f) =
MotionEvent.PointerCoords().apply {
this.x = x
this.y = y
+ setAxisValue(AXIS_HSCROLL, scrollX)
+ setAxisValue(AXIS_VSCROLL, scrollY)
}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
index 5f5158f..c972784 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/AndroidPointerInputTest.kt
@@ -28,6 +28,7 @@
import android.view.MotionEvent.ACTION_HOVER_MOVE
import android.view.MotionEvent.ACTION_MOVE
import android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT
+import android.view.MotionEvent.ACTION_SCROLL
import android.view.MotionEvent.ACTION_UP
import android.view.MotionEvent.TOOL_TYPE_FINGER
import android.view.MotionEvent.TOOL_TYPE_MOUSE
@@ -50,6 +51,7 @@
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.AbsoluteAlignment
import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.OpenComposeView
import androidx.compose.ui.composed
@@ -70,6 +72,7 @@
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.viewinterop.AndroidView
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -740,10 +743,24 @@
assertThat(event.type).isEqualTo(expectedHoverType)
}
+ @OptIn(ExperimentalComposeUiApi::class)
+ private fun assertScrollEvent(
+ event: PointerEvent,
+ scrollExpected: Offset
+ ) {
+ assertThat(event.changes).hasSize(1)
+ val change = event.changes[0]
+ assertThat(change.pressed).isFalse()
+ assertThat(event.type).isEqualTo(PointerEventType.Scroll)
+ // we agreed to reverse the delta in android to be in line with other platforms
+ assertThat(change.scrollDelta).isEqualTo(-scrollExpected)
+ }
+
private fun dispatchMouseEvent(
- action: Int = ACTION_HOVER_ENTER,
+ action: Int,
layoutCoordinates: LayoutCoordinates,
- offset: Offset = Offset.Zero
+ offset: Offset = Offset.Zero,
+ scrollDelta: Offset = Offset.Zero
) {
rule.runOnUiThread {
val root = layoutCoordinates.findRoot()
@@ -754,13 +771,14 @@
1,
0,
arrayOf(PointerProperties(0).also { it.toolType = MotionEvent.TOOL_TYPE_MOUSE }),
- arrayOf(PointerCoords(pos.x, pos.y))
+ arrayOf(PointerCoords(pos.x, pos.y, scrollDelta.x, scrollDelta.y))
)
val androidComposeView = findAndroidComposeView(container) as AndroidComposeView
when (action) {
ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT ->
androidComposeView.dispatchHoverEvent(event)
+ ACTION_SCROLL -> androidComposeView.dispatchGenericMotionEvent(event)
else -> androidComposeView.dispatchTouchEvent(event)
}
}
@@ -981,6 +999,149 @@
}
@Test
+ fun dispatchScroll() {
+ var layoutCoordinates: LayoutCoordinates? = null
+ val latch = CountDownLatch(1)
+ val events = mutableListOf<PointerEvent>()
+ val scrollDelta = Offset(0.35f, 0.65f)
+ rule.runOnUiThread {
+ container.setContent {
+ Box(
+ Modifier.fillMaxSize().onGloballyPositioned {
+ layoutCoordinates = it
+ latch.countDown()
+ }.pointerInput(Unit) {
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ event.changes[0].consumeAllChanges()
+ events += event
+ }
+ }
+ }
+ )
+ }
+ }
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ dispatchMouseEvent(ACTION_SCROLL, layoutCoordinates!!, scrollDelta = scrollDelta)
+ rule.runOnUiThread {
+ assertThat(events).hasSize(2) // synthetic enter and scroll
+ assertHoverEvent(events[0], isEnter = true)
+ assertScrollEvent(events[1], scrollExpected = scrollDelta)
+ }
+ }
+
+ @Test
+ fun dispatchScroll_whenButtonPressed() {
+ var layoutCoordinates: LayoutCoordinates? = null
+ val latch = CountDownLatch(1)
+ val events = mutableListOf<PointerEvent>()
+ val scrollDelta = Offset(0.35f, 0.65f)
+ rule.runOnUiThread {
+ container.setContent {
+ Box(
+ Modifier.fillMaxSize().onGloballyPositioned {
+ layoutCoordinates = it
+ latch.countDown()
+ }.pointerInput(Unit) {
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ event.changes[0].consumeAllChanges()
+ events += event
+ }
+ }
+ }
+ )
+ }
+ }
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ // press the button first before scroll
+ dispatchMouseEvent(ACTION_DOWN, layoutCoordinates!!)
+ dispatchMouseEvent(ACTION_SCROLL, layoutCoordinates!!, scrollDelta = scrollDelta)
+ rule.runOnUiThread {
+ assertThat(events).hasSize(3) // synthetic enter, button down, scroll
+ assertHoverEvent(events[0], isEnter = true)
+ assert(events[1].changes.fastAll { it.changedToDownIgnoreConsumed() })
+ assertScrollEvent(events[2], scrollExpected = scrollDelta)
+ }
+ }
+
+ @Test
+ fun dispatchScroll_batch() {
+ var layoutCoordinates: LayoutCoordinates? = null
+ val latch = CountDownLatch(1)
+ val events = mutableListOf<PointerEvent>()
+ val scrollDelta1 = Offset(0.32f, -0.75f)
+ val scrollDelta2 = Offset(0.14f, 0.35f)
+ val scrollDelta3 = Offset(-0.30f, -0.12f)
+ val scrollDelta4 = Offset(-0.05f, 0.68f)
+ rule.runOnUiThread {
+ container.setContent {
+ Box(
+ Modifier.fillMaxSize().onGloballyPositioned {
+ layoutCoordinates = it
+ latch.countDown()
+ }.pointerInput(Unit) {
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ event.changes[0].consumeAllChanges()
+ events += event
+ }
+ }
+ }
+ )
+ }
+ }
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ listOf(scrollDelta1, scrollDelta2, scrollDelta3, scrollDelta4).fastForEach {
+ dispatchMouseEvent(ACTION_SCROLL, layoutCoordinates!!, scrollDelta = it)
+ }
+ rule.runOnUiThread {
+ assertThat(events).hasSize(5) // 4 + synthetic enter
+ assertHoverEvent(events[0], isEnter = true)
+ assertScrollEvent(events[1], scrollExpected = scrollDelta1)
+ assertScrollEvent(events[2], scrollExpected = scrollDelta2)
+ assertScrollEvent(events[3], scrollExpected = scrollDelta3)
+ assertScrollEvent(events[4], scrollExpected = scrollDelta4)
+ }
+ }
+
+ @Test
+ fun mouseScroll_ignoredAsDownEvent() {
+ var layoutCoordinates: LayoutCoordinates? = null
+ val latch = CountDownLatch(1)
+ val events = mutableListOf<PointerEvent>()
+ val scrollDelta = Offset(0.35f, 0.65f)
+ rule.runOnUiThread {
+ container.setContent {
+ Box(
+ Modifier.fillMaxSize().onGloballyPositioned {
+ layoutCoordinates = it
+ latch.countDown()
+ }.pointerInput(Unit) {
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ event.changes[0].consumeAllChanges()
+ events += event
+ }
+ }
+ }
+ )
+ }
+ }
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ dispatchMouseEvent(ACTION_SCROLL, layoutCoordinates!!, scrollDelta = scrollDelta)
+ rule.runOnUiThread {
+ assertThat(events).hasSize(2) // hover enter + scroll
+ assertThat(events[1].changes).isNotEmpty()
+ assertThat(events[1].changes[0].changedToDown()).isFalse()
+ }
+ }
+
+ @Test
fun hoverEnterPressExitEnterExitRelease() {
var outerCoordinates: LayoutCoordinates? = null
var innerCoordinates: LayoutCoordinates? = null
@@ -1029,39 +1190,48 @@
@Test
fun hoverPressEnterRelease() {
- var outerCoordinates: LayoutCoordinates? = null
- var innerCoordinates: LayoutCoordinates? = null
+ var missCoordinates: LayoutCoordinates? = null
+ var hitCoordinates: LayoutCoordinates? = null
val latch = CountDownLatch(1)
val eventLog = mutableListOf<PointerEvent>()
rule.runOnUiThread {
container.setContent {
- Box(
- Modifier.fillMaxSize().onGloballyPositioned {
- outerCoordinates = it
- latch.countDown()
- }
- ) {
+ Box(Modifier.fillMaxSize()) {
Box(
- Modifier.align(Alignment.Center).size(50.dp).pointerInput(Unit) {
- awaitPointerEventScope {
- while (true) {
- val event = awaitPointerEvent()
- event.changes[0].consumeAllChanges()
- eventLog += event
+ Modifier.align(AbsoluteAlignment.TopLeft).size(50.dp)
+ .pointerInput(Unit) {
+ awaitPointerEventScope {
+ while (true) {
+ awaitPointerEvent()
+ }
}
+ }.onGloballyPositioned {
+ missCoordinates = it
+ latch.countDown()
}
- }.onGloballyPositioned { innerCoordinates = it }
+ )
+ Box(
+ Modifier.align(AbsoluteAlignment.BottomRight).size(50.dp)
+ .pointerInput(Unit) {
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ event.changes[0].consumeAllChanges()
+ eventLog += event
+ }
+ }
+ }.onGloballyPositioned { hitCoordinates = it }
)
}
}
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
- dispatchMouseEvent(ACTION_HOVER_ENTER, outerCoordinates!!)
- dispatchMouseEvent(ACTION_HOVER_EXIT, outerCoordinates!!)
- dispatchMouseEvent(ACTION_DOWN, outerCoordinates!!)
- dispatchMouseEvent(ACTION_MOVE, innerCoordinates!!)
- dispatchMouseEvent(ACTION_UP, innerCoordinates!!)
- dispatchMouseEvent(ACTION_HOVER_ENTER, innerCoordinates!!)
+ dispatchMouseEvent(ACTION_HOVER_ENTER, missCoordinates!!)
+ dispatchMouseEvent(ACTION_HOVER_EXIT, missCoordinates!!)
+ dispatchMouseEvent(ACTION_DOWN, missCoordinates!!)
+ dispatchMouseEvent(ACTION_MOVE, hitCoordinates!!)
+ dispatchMouseEvent(ACTION_UP, hitCoordinates!!)
+ dispatchMouseEvent(ACTION_HOVER_ENTER, hitCoordinates!!)
rule.runOnUiThread {
assertThat(eventLog).hasSize(1)
assertThat(eventLog[0].type).isEqualTo(PointerEventType.Enter)
@@ -1404,7 +1574,8 @@
// Hit the bottom box, but clipped
dispatchMouseEvent(ACTION_HOVER_MOVE, coords)
- dispatchMouseEvent(ACTION_HOVER_MOVE, coords,
+ dispatchMouseEvent(
+ ACTION_HOVER_MOVE, coords,
Offset(0f, (coords.size.height / 2 - 1).toFloat())
)
@@ -1413,17 +1584,20 @@
}
// Now hit the box in the unclipped region
- dispatchMouseEvent(ACTION_HOVER_MOVE, coords,
+ dispatchMouseEvent(
+ ACTION_HOVER_MOVE, coords,
Offset(0f, (coords.size.height / 2 + 1).toFloat())
)
// Now hit the bottom of the clipped region
- dispatchMouseEvent(ACTION_HOVER_MOVE, coords,
+ dispatchMouseEvent(
+ ACTION_HOVER_MOVE, coords,
Offset(0f, (coords.size.height - 1).toFloat())
)
// Now leave
- dispatchMouseEvent(ACTION_HOVER_MOVE, coords,
+ dispatchMouseEvent(
+ ACTION_HOVER_MOVE, coords,
Offset(0f, coords.size.height.toFloat() + 1f)
)
@@ -1570,6 +1744,71 @@
}
}
+ @Test
+ fun restartStreamAfterNotProcessing() {
+ // Stylus hover enter/exit events should be sent to pointer input areas
+ val eventLog = mutableListOf<PointerEvent>()
+ var hitCoordinates: LayoutCoordinates? = null
+ var missCoordinates: LayoutCoordinates? = null
+ val latch = CountDownLatch(2)
+ rule.runOnUiThread {
+ container.setContent {
+ Box(Modifier.fillMaxSize()) {
+ Box(Modifier.size(50.dp).align(AbsoluteAlignment.TopLeft)
+ .pointerInput(Unit) {
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ event.changes.forEach { it.consumeAllChanges() }
+ eventLog += event
+ }
+ }
+ }.onGloballyPositioned {
+ hitCoordinates = it
+ latch.countDown()
+ }
+ )
+ Box(Modifier.size(50.dp).align(AbsoluteAlignment.BottomRight)
+ .onGloballyPositioned {
+ missCoordinates = it
+ latch.countDown()
+ }
+ )
+ }
+ }
+ }
+ assertTrue(latch.await(1, TimeUnit.SECONDS))
+ val miss = missCoordinates!!
+ val hit = hitCoordinates!!
+
+ // This should hit
+ dispatchTouchEvent(ACTION_DOWN, hit)
+ dispatchTouchEvent(ACTION_UP, hit)
+
+ // This should miss
+ dispatchTouchEvent(ACTION_DOWN, miss)
+
+ // This should hit
+ dispatchTouchEvent(ACTION_DOWN, hit)
+
+ rule.runOnUiThread {
+ assertThat(eventLog).hasSize(3)
+ val down1 = eventLog[0]
+ val up1 = eventLog[1]
+ val down2 = eventLog[2]
+ assertThat(down1.changes).hasSize(1)
+ assertThat(up1.changes).hasSize(1)
+ assertThat(down2.changes).hasSize(1)
+
+ assertThat(down1.type).isEqualTo(PointerEventType.Press)
+ assertThat(up1.type).isEqualTo(PointerEventType.Release)
+ assertThat(down2.type).isEqualTo(PointerEventType.Press)
+
+ assertThat(up1.changes[0].id).isEqualTo(down1.changes[0].id)
+ assertThat(down2.changes[0].id.value).isEqualTo(down1.changes[0].id.value + 2)
+ }
+ }
+
private fun createPointerEventAt(eventTime: Int, action: Int, locationInWindow: IntArray) =
MotionEvent(
eventTime,
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterAndroidViewHookupTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterAndroidViewHookupTest.kt
index 59d7e44..51e8a95 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterAndroidViewHookupTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/pointer/PointerInteropFilterAndroidViewHookupTest.kt
@@ -34,6 +34,7 @@
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.viewinterop.AndroidView
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.FlakyTest
import androidx.test.filters.MediumTest
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -477,6 +478,7 @@
assertThat(captureRequestDisallow.disallowIntercept).isFalse()
}
+ @FlakyTest(bugId = 206967867)
@Test
fun disallowTriggeredWhenMovementInClickChildAfterRequestDisallow() {
var clicked = false
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/NodesRemeasuredOnceTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/NodesRemeasuredOnceTest.kt
index 51ee357..28bb807 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/NodesRemeasuredOnceTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/NodesRemeasuredOnceTest.kt
@@ -119,6 +119,34 @@
assertThat(remeasurements).isEqualTo(2)
}
}
+
+ @Test
+ fun remeasuringChildWithExtraLayer_notPlacedChild() {
+ val height = mutableStateOf(10)
+ var remeasurements = 0
+
+ rule.setContent {
+ WrapChild(onMeasured = { actualHeight ->
+ assertThat(actualHeight).isEqualTo(height.value)
+ remeasurements++
+ }) {
+ NotPlaceChild(height) {
+ WrapChild {
+ Child(height)
+ }
+ }
+ }
+ }
+
+ rule.runOnIdle {
+ assertThat(remeasurements).isEqualTo(1)
+ height.value = 20
+ }
+
+ rule.runOnIdle {
+ assertThat(remeasurements).isEqualTo(2)
+ }
+ }
}
@Composable
@@ -134,6 +162,16 @@
}
@Composable
+private fun NotPlaceChild(height: State<Int>, content: @Composable () -> Unit) {
+ Layout(content = content) { measurables, constraints ->
+ layout(constraints.maxWidth, height.value) {
+ measurables.first()
+ .measure(constraints.copy(minHeight = 0, maxHeight = Constraints.Infinity))
+ }
+ }
+}
+
+@Composable
private fun Child(height: State<Int>) {
Layout { _, constraints ->
layout(constraints.maxWidth, height.value) {}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/LayoutNodeWrapperInitializationTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/LayoutNodeWrapperInitializationTest.kt
index a72033a..0436ac3 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/LayoutNodeWrapperInitializationTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/node/LayoutNodeWrapperInitializationTest.kt
@@ -23,6 +23,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusModifier
import androidx.compose.ui.focus.FocusStateImpl
+import androidx.compose.ui.focus.focusTarget
import androidx.compose.ui.input.key.KeyInputModifier
import androidx.compose.ui.input.pointer.PointerInputModifier
import androidx.compose.ui.input.pointer.PointerInteropFilter
@@ -49,7 +50,7 @@
// Act.
rule.setContent {
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(focusModifier))
}
// Assert.
@@ -117,7 +118,7 @@
rule.setContent {
scope = currentRecomposeScope
focusModifier = FocusModifier(FocusStateImpl.Inactive)
- Box(modifier = focusModifier)
+ Box(Modifier.focusTarget(focusModifier))
}
// Act.
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt
index 6193daf..f6c3140 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/MotionEventAdapter.android.kt
@@ -27,6 +27,7 @@
import android.view.MotionEvent.ACTION_HOVER_MOVE
import android.view.MotionEvent.ACTION_POINTER_DOWN
import android.view.MotionEvent.ACTION_POINTER_UP
+import android.view.MotionEvent.ACTION_SCROLL
import android.view.MotionEvent.ACTION_UP
import android.view.MotionEvent.TOOL_TYPE_ERASER
import android.view.MotionEvent.TOOL_TYPE_FINGER
@@ -95,6 +96,7 @@
val isHover = action == ACTION_HOVER_EXIT || action == ACTION_HOVER_MOVE ||
action == ACTION_HOVER_ENTER
+ val isScroll = action == ACTION_SCROLL
if (isHover) {
val hoverId = motionEvent.getPointerId(motionEvent.actionIndex)
@@ -117,7 +119,11 @@
positionCalculator,
motionEvent,
i,
- !isHover && i != upIndex
+ // "pressed" means:
+ // 1. we're not hovered
+ // 2. we didn't get UP event for a pointer
+ // 3. button on the mouse is pressed BUT it's not a "scroll" simulated button
+ !isHover && i != upIndex && (!isScroll || motionEvent.buttonState != 0)
)
)
}
@@ -132,6 +138,15 @@
}
/**
+ * An ACTION_DOWN or ACTION_POINTER_DOWN was received, but not handled, so the stream should
+ * be considered ended.
+ */
+ fun endStream(pointerId: Int) {
+ canHover.delete(pointerId)
+ motionEventToComposePointerIdMap.delete(pointerId)
+ }
+
+ /**
* Add any new pointer IDs.
*/
private fun addFreshIds(motionEvent: MotionEvent) {
@@ -167,7 +182,7 @@
val actionIndex = motionEvent.actionIndex
val pointerId = motionEvent.getPointerId(actionIndex)
if (!canHover.get(pointerId, false)) {
- motionEventToComposePointerIdMap.delete(motionEvent.getPointerId(actionIndex))
+ motionEventToComposePointerIdMap.delete(pointerId)
canHover.delete(pointerId)
}
}
@@ -277,6 +292,17 @@
}
}
}
+ val scrollDelta = if (motionEvent.actionMasked == ACTION_SCROLL) {
+ val x = motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL)
+ val y = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL)
+ // NOTE: we revert the scroll offset because android is special compared to other
+ // platforms and send the reversed sign for up and down mouse wheel scroll. In order to
+ // support better x-platform mouse scroll, we revert to be in line with desktop
+ // platforms.
+ Offset(x, y) * -1f
+ } else {
+ Offset.Zero
+ }
val issuesEnterExit = canHover.get(motionEvent.getPointerId(index), false)
return PointerInputEventData(
@@ -287,7 +313,8 @@
pressed,
toolType,
issuesEnterExit,
- historical
+ historical,
+ scrollDelta
)
}
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.android.kt
index fde4a0a..82ac8cb 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.android.kt
@@ -18,6 +18,8 @@
import android.view.KeyEvent
import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_SCROLL
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMap
@@ -49,6 +51,7 @@
actual var type: PointerEventType = calculatePointerEventType()
internal set
+ @OptIn(ExperimentalComposeUiApi::class)
private fun calculatePointerEventType(): PointerEventType {
val motionEvent = motionEvent
if (motionEvent != null) {
@@ -61,6 +64,7 @@
MotionEvent.ACTION_MOVE -> PointerEventType.Move
MotionEvent.ACTION_HOVER_ENTER -> PointerEventType.Enter
MotionEvent.ACTION_HOVER_EXIT -> PointerEventType.Exit
+ ACTION_SCROLL -> PointerEventType.Scroll
else -> PointerEventType.Unknown
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEvent.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEvent.android.kt
index c3ee76b..3862b31 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEvent.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/input/pointer/PointerInputEvent.android.kt
@@ -22,4 +22,4 @@
actual val uptime: Long,
actual val pointers: List<PointerInputEventData>,
val motionEvent: MotionEvent
-)
\ No newline at end of file
+)
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 9c3862d..45b6659 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
@@ -32,7 +32,9 @@
import android.view.MotionEvent.ACTION_HOVER_ENTER
import android.view.MotionEvent.ACTION_HOVER_EXIT
import android.view.MotionEvent.ACTION_HOVER_MOVE
+import android.view.MotionEvent.ACTION_SCROLL
import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_POINTER_DOWN
import android.view.MotionEvent.ACTION_POINTER_UP
import android.view.MotionEvent.ACTION_UP
import android.view.MotionEvent.TOOL_TYPE_MOUSE
@@ -590,15 +592,15 @@
view,
object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(
- host: View?,
- info: AccessibilityNodeInfoCompat?
+ host: View,
+ info: AccessibilityNodeInfoCompat
) {
super.onInitializeAccessibilityNodeInfo(host, info)
var parentId = SemanticsNode(layoutNode.outerSemantics!!, false).parent!!.id
if (parentId == semanticsOwner.unmergedRootSemanticsNode.id) {
parentId = AccessibilityNodeProviderCompat.HOST_VIEW_ID
}
- info!!.setParent(thisView, parentId)
+ info.setParent(thisView, parentId)
}
}
)
@@ -1016,6 +1018,14 @@
if (autofillSupported()) _autofill?.performAutofill(values)
}
+ override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
+ return if (event.actionMasked == ACTION_SCROLL) {
+ handleMotionEvent(event).dispatchedToAPointerInputModifier
+ } else {
+ super.dispatchGenericMotionEvent(event)
+ }
+ }
+
// TODO(shepshapard): Test this method.
override fun dispatchTouchEvent(motionEvent: MotionEvent): Boolean {
if (hoverExitReceived) {
@@ -1142,11 +1152,21 @@
lastDownPointerPosition = it
}
- pointerInputEventProcessor.process(
+ val result = pointerInputEventProcessor.process(
pointerInputEvent,
this,
isInBounds(motionEvent)
)
+ val action = motionEvent.actionMasked
+ if ((action == ACTION_DOWN || action == ACTION_POINTER_DOWN) &&
+ !result.dispatchedToAPointerInputModifier
+ ) {
+ // We aren't handling the pointer, so the event stream has ended for us.
+ // The next time we receive a pointer event, it should be considered a new
+ // pointer.
+ motionEventAdapter.endStream(motionEvent.getPointerId(motionEvent.actionIndex))
+ }
+ result
} else {
pointerInputEventProcessor.processCancel()
ProcessResult(
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 64cd3cf..e413d5f 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
@@ -1238,7 +1238,7 @@
AccessibilityNodeInfoCompat.ACTION_FOCUS -> {
if (node.unmergedConfig.getOrNull(SemanticsProperties.Focused) == false) {
node.layoutNode.outerLayoutNodeWrapper.findLastFocusWrapper()
- ?.requestFocus(propagateFocus = false) ?: return false
+ ?.requestFocus() ?: return false
return true
} else {
return false
@@ -1545,7 +1545,7 @@
sendEventForVirtualView(previousVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)
}
- override fun getAccessibilityNodeProvider(host: View?): AccessibilityNodeProviderCompat {
+ override fun getAccessibilityNodeProvider(host: View): AccessibilityNodeProviderCompat {
return nodeProvider
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
index 9e39112..086de49 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/Modifier.kt
@@ -125,7 +125,7 @@
*
* @sample androidx.compose.ui.samples.ModifierParameterSample
*/
- // The companion object implements `Modifier` so that it may be used as the start of a
+ // The companion object implements `Modifier` so that it may be used as the start of a
// modifier extension factory expression.
companion object : Modifier {
override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = initial
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/TempListUtils.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/TempListUtils.kt
index 940317c..d5c4d7c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/TempListUtils.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/TempListUtils.kt
@@ -27,6 +27,10 @@
* to each pair of two adjacent elements in this collection.
*
* The returned list is empty if this collection contains less than two elements.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R> List<T>.fastZipWithNext(transform: (T, T) -> R): List<R> {
@@ -50,6 +54,10 @@
* Throws an exception if this collection is empty. If the collection can be empty in an expected
* way, please use [reduceOrNull] instead. It returns `null` when its receiver is empty.
*
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
+ *
* @param [operation] function that takes current accumulator value and an element,
* and calculates the next accumulator value.
*/
@@ -71,6 +79,10 @@
* If any of two pairs would have the same key the last one gets added to the map.
*
* The returned map preserves the entry iteration order of the original collection.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, K, V> List<T>.fastAssociate(transform: (T) -> Pair<K, V>): Map<K, V> {
@@ -86,6 +98,10 @@
* Returns a list of values built from the elements of `this` collection and the [other] collection with the same index
* using the provided [transform] function applied to each pair of elements.
* The returned list has length of the shortest collection.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R, V> List<T>.fastZip(
@@ -104,6 +120,10 @@
/**
* Returns a list containing the results of applying the given [transform] function
* to each element in the original collection.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R> List<T>.fastMapNotNull(transform: (T) -> R?): List<R> {
@@ -122,6 +142,10 @@
* If the collection could be huge, you can specify a non-negative value of [limit], in which case
* only the first [limit] elements will be appended, followed by the [truncated] string (which
* defaults to "...").
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
internal fun <T> List<T>.fastJoinToString(
separator: CharSequence = ", ",
@@ -142,6 +166,10 @@
* If the collection could be huge, you can specify a non-negative value of [limit], in which
* case only the first [limit] elements will be appended, followed by the [truncated] string
* (which defaults to "...").
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
private fun <T, A : Appendable> List<T>.fastJoinTo(
buffer: A,
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt
index b9c3df2..70c6454 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusManager.kt
@@ -70,7 +70,7 @@
*/
val modifier: Modifier
// TODO(b/168831247): return an empty Modifier when there are no focusable children.
- get() = focusModifier
+ get() = Modifier.focusTarget(focusModifier)
lateinit var layoutDirection: LayoutDirection
@@ -152,29 +152,29 @@
if (destination == null) {
// Check if we need to wrap around (no destination and a non-root item is focused)
if (focusModifier.focusState.hasFocus && !focusModifier.focusState.isFocused) {
- // Next and Previous wraps around.
- return when (focusDirection) {
- Next, Previous -> {
- // Clear Focus to send focus the root node.
- // Wrap around by requesting focus for the root and then calling moveFocus.
- clearFocus(force = false)
+ // Next and Previous wraps around.
+ return when (focusDirection) {
+ Next, Previous -> {
+ // Clear Focus to send focus the root node.
+ // Wrap around by requesting focus for the root and then calling moveFocus.
+ clearFocus(force = false)
- if (focusModifier.focusState.isFocused) {
- moveFocus(focusDirection)
- } else {
- false
- }
+ if (focusModifier.focusState.isFocused) {
+ moveFocus(focusDirection)
+ } else {
+ false
}
- else -> false
}
+ else -> false
+ }
}
return false
}
checkNotNull(destination.findParentFocusNode()) { "Move focus landed at the root." }
- // If we found a potential next item, call requestFocus() to move focus to it.
- destination.requestFocus(propagateFocus = false)
+ // If we found a potential next item, move focus to it.
+ destination.requestFocus()
return true
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusModifier.kt
index b96a96a..ef8a6da 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusModifier.kt
@@ -42,9 +42,7 @@
// using this internal constructor.
inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo
) : ModifierLocalConsumer,
- ModifierLocalProvider<FocusProperties>,
InspectorValueInfo(inspectorInfo) {
-
// TODO(b/188684110): Move focusState and focusedChild to ModifiedFocusNode and make this
// modifier stateless.
var focusState: FocusStateImpl = initialFocus
@@ -63,12 +61,6 @@
focusNode.setUpdatedProperties(ModifierLocalFocusProperties.current)
}
}
-
- override val key = ModifierLocalFocusProperties
-
- // Writing the FocusProperties ModifierLocal so that any child focus modifiers don't read
- // properties that were meant for this focus modifier.
- override val value = DefaultFocusProperties
}
/**
@@ -85,7 +77,8 @@
* @sample androidx.compose.ui.samples.FocusableSampleUsingLowerLevelFocusTarget
*/
fun Modifier.focusTarget(): Modifier = composed(debugInspectorInfo { name = "focusTarget" }) {
- remember { FocusModifier(Inactive).then(NoFocusListener) }
+ val focusModifier = remember { FocusModifier(Inactive) }
+ focusTarget(focusModifier)
}
/**
@@ -96,7 +89,17 @@
ReplaceWith("focusTarget()", "androidx.compose.ui.focus.focusTarget")
)
fun Modifier.focusModifier(): Modifier = composed(debugInspectorInfo { name = "focusModifier" }) {
- remember { FocusModifier(Inactive).then(NoFocusListener) }
+ val focusModifier = remember { FocusModifier(Inactive) }
+ focusTarget(focusModifier)
+}
+
+/**
+ * A helper function that allows you to pass in an instance of FocusModifier.
+ * This is only used internally, to set the root focus modifier or in tests where we need to set an
+ * initial focus state or inspect the focus modifier state after running some operation.
+ */
+internal fun Modifier.focusTarget(focusModifier: FocusModifier): Modifier {
+ return this.then(focusModifier).then(ResetFocusModifierLocals)
}
/**
@@ -107,11 +110,47 @@
internal val ModifierLocalHasFocusEventListener = modifierLocalOf { false }
/**
- * A modifier that updates the HasFocusEventListener modifier local value to false.
- * This is used after every focusModifier because the listeners that appeared before
- * are not applicable further down the hierarchy.
+ * The Focus Modifier reads the state of some Modifier Locals that are set by the parents. Consider
+ * the following example:
+ *
+ * Box(
+ * Modifier
+ * .focusRequester(item1)
+ * .onFocusChanged { ... }
+ * .focusOrder { next = item2 }
+ * .focusProperties { canFocus = false }
+ * .focusTarget() // focusModifier1
+ * ) {
+ * Box(
+ * Modifier.focusTarget() // focusModifier2
+ * )
+ * }
+ *
+ * Here, the focusRequester, onFocusChanged, focusOrder and focusProperties modifiers provide
+ * modifier local values that are intended for focusModifier1.
+ *
+ * We don't want these modifier locals to be read by focusModifier2.
+ *
+ * Add this modifier after every FocusModifier to reset all the focus related modifier locals, so
+ * that focus modifiers further down the tree do not read these values.
*/
-internal object NoFocusListener : ModifierLocalProvider<Boolean> {
- override val key: ProvidableModifierLocal<Boolean> get() = ModifierLocalHasFocusEventListener
- override val value: Boolean get() = false
-}
+internal val ResetFocusModifierLocals: Modifier = Modifier
+ // Reset the FocusProperties modifier local.
+ .then(
+ object : ModifierLocalProvider<FocusProperties> {
+ override val key: ProvidableModifierLocal<FocusProperties>
+ get() = ModifierLocalFocusProperties
+ override val value: FocusProperties
+ get() = DefaultFocusProperties
+ }
+
+ )
+ // Update the HasFocusEventListener modifier local value to false.
+ .then(
+ object : ModifierLocalProvider<Boolean> {
+ override val key: ProvidableModifierLocal<Boolean>
+ get() = ModifierLocalHasFocusEventListener
+ override val value: Boolean
+ get() = false
+ }
+ )
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequester.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequester.kt
index 7f0d6f9..c5b77d3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequester.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusRequester.kt
@@ -52,7 +52,7 @@
*/
fun requestFocus() {
check(focusRequesterNodes.isNotEmpty()) { focusRequesterNotInitialized }
- focusRequesterNodes.forEach { it.findFocusNode()?.requestFocus(propagateFocus = false) }
+ focusRequesterNodes.forEach { it.findFocusNode()?.requestFocus() }
}
/**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactions.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactions.kt
index 6399c69..7de10b2 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactions.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/focus/FocusTransactions.kt
@@ -27,44 +27,26 @@
/**
* Request focus for this node.
*
- * @param propagateFocus Whether the focus should be propagated to the node's children.
- *
* In Compose, the parent [FocusNode][ModifiedFocusNode] controls focus for its focusable
* children. Calling this function will send a focus request to this
* [FocusNode][ModifiedFocusNode]'s parent [FocusNode][ModifiedFocusNode].
*/
-internal fun ModifiedFocusNode.requestFocus(propagateFocus: Boolean = true) {
+internal fun ModifiedFocusNode.requestFocus() {
when (focusState) {
Active, Captured, Deactivated, DeactivatedParent -> {
// There is no change in focus state, but we send a focus event to notify the user
// that the focus request is completed.
sendOnFocusEvent(focusState)
}
- ActiveParent -> {
- val currentFocusedChild = focusedChild
- requireNotNull(currentFocusedChild)
+ ActiveParent -> if (clearChildFocus()) grantFocus()
- // We don't need to do anything if [propagateFocus] is true,
- // since this subtree already has focus.
- if (propagateFocus) {
- sendOnFocusEvent(focusState)
- return
- }
-
- if (currentFocusedChild.clearFocus()) {
- grantFocus(propagateFocus)
- focusedChild = null
- }
- }
Inactive -> {
val focusParent = findParentFocusNode()
- if (focusParent == null) {
- if (requestFocusForOwner()) {
- grantFocus(propagateFocus)
+ if (focusParent != null) {
+ focusParent.requestFocusForChild(this)
+ } else if (requestFocusForOwner()) {
+ grantFocus()
}
- } else {
- focusParent.requestFocusForChild(this, propagateFocus)
- }
}
}
}
@@ -150,30 +132,23 @@
* If the node is [ActiveParent], we need to clear focus from the [Active] descendant
* first, before clearing focus from this node.
*/
- ActiveParent -> {
- val currentFocusedChild = focusedChild
- requireNotNull(currentFocusedChild)
- currentFocusedChild.clearFocus(forcedClear).also { success ->
- if (success) {
- focusState = Inactive
- focusedChild = null
- }
- }
+ ActiveParent -> if (clearChildFocus()) {
+ focusState = Inactive
+ true
+ } else {
+ false
}
/**
* If the node is [DeactivatedParent], we need to clear focus from the [Active] descendant
* first, before clearing focus from this node.
*/
- DeactivatedParent -> {
- val currentFocusedChild = focusedChild
- requireNotNull(currentFocusedChild)
- currentFocusedChild.clearFocus(forcedClear).also { success ->
- if (success) {
- focusState = Deactivated
- focusedChild = null
- }
- }
+ DeactivatedParent -> if (clearChildFocus()) {
+ focusState = Deactivated
+ true
+ } else {
+ false
}
+
/**
* If the node is [Captured], deny requests to clear focus, except for a forced clear.
*/
@@ -190,38 +165,34 @@
}
}
-/**
- * This function grants focus to this node.
- *
- * @param propagateFocus Whether the focus should be propagated to the node's children.
- *
- * Note: This function is private, and should only be called by a parent [ModifiedFocusNode] to
- * grant focus to one of its child [ModifiedFocusNode]s.
- */
-private fun ModifiedFocusNode.grantFocus(propagateFocus: Boolean) {
+// This function grants focus to this node.
+// Note: This is a private function that just changes the state of this node and does not affect any
+// other nodes in the hierarchy.
+private fun ModifiedFocusNode.grantFocus() {
+ // No Focused Children, or we don't want to propagate focus to children.
+ focusState = when (focusState) {
+ Inactive, Active, ActiveParent -> Active
+ Captured -> Captured
+ Deactivated, DeactivatedParent -> error("Granting focus to a deactivated node.")
+ }
+}
- // TODO (b/144126570) use ChildFocusability.
- // For now we assume children get focus before parent).
+// This function grants focus to the specified child.
+// Note: This is a private function and should only be called by a parent to grant focus to one of
+// its child. It does not affect any other nodes in the hierarchy.
+private fun ModifiedFocusNode.grantFocusToChild(childNode: ModifiedFocusNode): Boolean {
+ childNode.grantFocus()
+ focusedChild = childNode
+ return true
+}
- // TODO (b/144126759): Design a system to decide which child gets focus.
- // for now we grant focus to the first child.
- val focusedCandidate = focusableChildren(excludeDeactivated = false).firstOrNull()
-
- if (focusedCandidate == null || !propagateFocus) {
- // No Focused Children, or we don't want to propagate focus to children.
- focusState = when (focusState) {
- Inactive, Active, ActiveParent -> Active
- Captured -> Captured
- Deactivated, DeactivatedParent -> error("Granting focus to a deactivated node.")
- }
+// This function clears any focus from the focused child.
+private fun ModifiedFocusNode.clearChildFocus(): Boolean {
+ return if (requireNotNull(focusedChild).clearFocus()) {
+ focusedChild = null
+ true
} else {
- focusState = when (focusState) {
- Inactive, Active, ActiveParent -> ActiveParent
- Captured -> { Captured; return }
- Deactivated, DeactivatedParent -> DeactivatedParent
- }
- focusedChild = focusedCandidate
- focusedCandidate.grantFocus(propagateFocus)
+ false
}
}
@@ -230,61 +201,35 @@
* focus.
*
* @param childNode: The node that is requesting focus.
- * @param propagateFocus Whether the focus should be propagated to the node's children.
* @return true if focus was granted, false otherwise.
*/
-private fun ModifiedFocusNode.requestFocusForChild(
- childNode: ModifiedFocusNode,
- propagateFocus: Boolean
-): Boolean {
+private fun ModifiedFocusNode.requestFocusForChild(childNode: ModifiedFocusNode): Boolean {
// Only this node's children can ask for focus.
if (!focusableChildren(excludeDeactivated = false).contains(childNode)) {
error("Non child node cannot request focus.")
}
- return when (focusState) {
+ return when (focusState) {
/**
* If this node is [Active], it can give focus to the requesting child.
*/
Active -> {
focusState = ActiveParent
- focusedChild = childNode
- childNode.grantFocus(propagateFocus)
- true
+ grantFocusToChild(childNode)
}
/**
* If this node is [ActiveParent] ie, one of the parent's descendants is [Active],
* remove focus from the currently focused child and grant it to the requesting child.
*/
- ActiveParent -> {
- val previouslyFocusedNode = focusedChild
- requireNotNull(previouslyFocusedNode)
- if (previouslyFocusedNode.clearFocus()) {
- focusedChild = childNode
- childNode.grantFocus(propagateFocus)
- true
- } else {
- // Currently focused component does not want to give up focus.
- false
- }
- }
- DeactivatedParent -> {
- val previouslyFocusedNode = focusedChild
- if (previouslyFocusedNode == null) {
- // we use DeactivatedParent and focusedchild == null to indicate an intermediate
- // state where a parent requested focus so that it can transfer it to a child.
- focusedChild = childNode
- childNode.grantFocus(propagateFocus)
- true
- } else if (previouslyFocusedNode.clearFocus()) {
- focusedChild = childNode
- childNode.grantFocus(propagateFocus)
- true
- } else {
- // Currently focused component does not want to give up focus.
- false
- }
+ ActiveParent -> if (clearChildFocus()) grantFocusToChild(childNode) else false
+
+ DeactivatedParent -> when {
+ // DeactivatedParent && NoFocusChild is used to indicate an intermediate state where
+ // this parent requested focus so that it can transfer it to a child.
+ focusedChild == null -> grantFocusToChild(childNode)
+ clearChildFocus() -> grantFocusToChild(childNode)
+ else -> false
}
/**
* If this node is not [Active], we must gain focus first before granting it
@@ -292,19 +237,18 @@
*/
Inactive -> {
val focusParent = findParentFocusNode()
- if (focusParent == null) {
- // If the owner successfully gains focus, proceed otherwise return false.
- if (requestFocusForOwner()) {
+ when {
+ // If this node is the root, request focus from the compose owner.
+ focusParent == null && requestFocusForOwner() -> {
focusState = Active
- requestFocusForChild(childNode, propagateFocus)
- } else {
- false
+ requestFocusForChild(childNode)
}
- } else if (focusParent.requestFocusForChild(this, propagateFocus = false)) {
- requestFocusForChild(childNode, propagateFocus)
- } else {
+ // For non-root nodes, request focus for this node before the child.
+ focusParent != null && focusParent.requestFocusForChild(this) ->
+ requestFocusForChild(childNode)
+
// Could not gain focus, so have no focus to give.
- false
+ else -> false
}
}
/**
@@ -317,7 +261,7 @@
*/
Deactivated -> {
activateNode()
- val childGrantedFocus = requestFocusForChild(childNode, propagateFocus)
+ val childGrantedFocus = requestFocusForChild(childNode)
deactivateNode()
childGrantedFocus
}
@@ -325,7 +269,5 @@
}
private fun ModifiedFocusNode.requestFocusForOwner(): Boolean {
- val owner = layoutNode.owner
- requireNotNull(owner, { "Owner not initialized." })
- return owner.requestFocus()
+ return layoutNode.owner?.requestFocus() ?: error("Owner not initialized.")
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/InternalPointerInput.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/InternalPointerInput.kt
index b0d20eb..4a64979 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/InternalPointerInput.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/InternalPointerInput.kt
@@ -47,7 +47,8 @@
val down: Boolean,
val type: PointerType,
val issuesEnterExit: Boolean = false,
- val historical: List<HistoricalChange> = mutableListOf()
+ val historical: List<HistoricalChange> = mutableListOf(),
+ val scrollDelta: Offset = Offset.Zero
)
/**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.kt
index 53e6827..34632aa 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.kt
@@ -27,7 +27,6 @@
import androidx.compose.ui.input.pointer.PointerEventPass.Initial
import androidx.compose.ui.input.pointer.PointerEventPass.Main
import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.node.InternalCoreApi
import androidx.compose.ui.unit.IntSize
/**
@@ -108,7 +107,7 @@
/**
* Describes a pointer input change event that has occurred at a particular point in time.
*/
-expect class PointerEvent @OptIn(InternalCoreApi::class) internal constructor(
+expect class PointerEvent internal constructor(
changes: List<PointerInputChange>,
internalPointerEvent: InternalPointerEvent?
) {
@@ -263,7 +262,7 @@
/**
* The device type that produces a [PointerInputChange], such as a mouse or stylus.
*/
-inline class PointerType internal constructor(private val value: Int) {
+inline class PointerType private constructor(private val value: Int) {
override fun toString(): String = when (value) {
1 -> "Touch"
@@ -304,7 +303,7 @@
/**
* Indicates the primary reason that the [PointerEvent] was sent.
*/
-inline class PointerEventType(internal val value: Int) {
+inline class PointerEventType private constructor(internal val value: Int) {
companion object {
/**
* An unknown reason for the event.
@@ -344,14 +343,25 @@
* [Enter], [Exit], and [Enter] will be received.
*/
val Exit = PointerEventType(5)
+
+ /**
+ * A scroll event was sent. This can happen, for example, due to a mouse scroll wheel.
+ * This event indicates that the [PointerInputChange.scrollDelta]'s [Offset] is non-zero.
+ */
+ @Suppress("EXPERIMENTAL_ANNOTATION_ON_WRONG_TARGET")
+ @ExperimentalComposeUiApi
+ @get:ExperimentalComposeUiApi
+ val Scroll = PointerEventType(6)
}
+ @OptIn(ExperimentalComposeUiApi::class)
override fun toString(): String = when (this) {
Press -> "Press"
Release -> "Release"
Move -> "Move"
Enter -> "Enter"
Exit -> "Exit"
+ Scroll -> "Scroll"
else -> "Unknown"
}
}
@@ -419,8 +429,41 @@
get() = _historical ?: listOf()
private var _historical: List<HistoricalChange>? = null
+ /**
+ * The amount of scroll wheel movement in the horizontal and vertical directions.
+ */
+ @Suppress("EXPERIMENTAL_ANNOTATION_ON_WRONG_TARGET")
@ExperimentalComposeUiApi
- constructor(
+ @get:ExperimentalComposeUiApi
+ val scrollDelta: Offset
+ get() = _scrollDelta
+ private var _scrollDelta: Offset = Offset.Zero
+
+ /**
+ * Indicates whether the change was consumed or not. Note that the change must be consumed in
+ * full as there's no partial consumption system provided.
+ */
+ @Suppress("EXPERIMENTAL_ANNOTATION_ON_WRONG_TARGET")
+ @ExperimentalComposeUiApi
+ @get:ExperimentalComposeUiApi
+ val isConsumed: Boolean
+ get() = consumed.downChange || consumed.positionChange
+
+ /**
+ * Consume change event, claiming all the corresponding change info to the caller. This is
+ * usually needed when, button, when being clicked, consumed the "up" event so no other parents
+ * of this button could consume this "up" again.
+ *
+ * "Consumption" is just an indication of the claim and each pointer input handler
+ * implementation must manually check this flag to respect it.
+ */
+ @ExperimentalComposeUiApi
+ fun consume() {
+ consumed.downChange = true
+ consumed.positionChange = true
+ }
+
+ internal constructor(
id: PointerId,
uptimeMillis: Long,
position: Offset,
@@ -430,7 +473,8 @@
previousPressed: Boolean,
consumed: ConsumedData,
type: PointerType,
- historical: List<HistoricalChange>
+ historical: List<HistoricalChange>,
+ scrollDelta: Offset,
) : this(
id,
uptimeMillis,
@@ -443,6 +487,7 @@
type
) {
_historical = historical
+ _scrollDelta = scrollDelta
}
fun copy(
@@ -465,7 +510,8 @@
previousPressed,
consumed,
type,
- this.historical
+ this.historical,
+ this.scrollDelta
)
@ExperimentalComposeUiApi
@@ -490,7 +536,34 @@
previousPressed,
consumed,
type,
- historical
+ historical,
+ this.scrollDelta
+ )
+
+ internal fun copy(
+ id: PointerId = this.id,
+ currentTime: Long = this.uptimeMillis,
+ currentPosition: Offset = this.position,
+ currentPressed: Boolean = this.pressed,
+ previousTime: Long = this.previousUptimeMillis,
+ previousPosition: Offset = this.previousPosition,
+ previousPressed: Boolean = this.previousPressed,
+ consumed: ConsumedData = this.consumed,
+ type: PointerType = this.type,
+ historical: List<HistoricalChange> = this.historical,
+ scrollDelta: Offset
+ ): PointerInputChange = PointerInputChange(
+ id,
+ currentTime,
+ currentPosition,
+ currentPressed,
+ previousTime,
+ previousPosition,
+ previousPressed,
+ consumed,
+ type,
+ historical,
+ scrollDelta
)
override fun toString(): String {
@@ -503,7 +576,8 @@
"previousPressed=$previousPressed, " +
"consumed=$consumed, " +
"type=$type, " +
- "historical=$historical)"
+ "historical=$historical," +
+ "scrollDelta=$scrollDelta)"
}
}
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 e5f1347..20cf960 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
@@ -147,7 +147,8 @@
previousDown,
ConsumedData(),
it.type,
- it.historical
+ it.historical,
+ it.scrollDelta
)
if (it.down) {
previousPointerInputData[it.id] = PointerInputData(
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
index d3d6f92..3c08f7a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
@@ -60,11 +60,8 @@
}
}
- /**
- * An initialization function that is called when the [LayoutNodeWrapper] is initially created,
- * and also called when the [LayoutNodeWrapper] is re-used.
- */
- open fun onInitialize() {
+ override fun onInitialize() {
+ super.onInitialize()
wrapped.wrappedBy = this
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedDrawNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DrawEntity.kt
similarity index 74%
rename from compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedDrawNode.kt
rename to compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DrawEntity.kt
index 3f8536f..aa8d3a2c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifiedDrawNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DrawEntity.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -21,13 +21,21 @@
import androidx.compose.ui.draw.DrawModifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.toSize
-internal class ModifiedDrawNode(
- wrapped: LayoutNodeWrapper,
- drawModifier: DrawModifier
-) : DelegatingLayoutNodeWrapper<DrawModifier>(wrapped, drawModifier), OwnerScope {
+internal class DrawEntity(
+ val layoutNodeWrapper: LayoutNodeWrapper,
+ val modifier: DrawModifier
+) : OwnerScope {
+ private val layoutNode: LayoutNode
+ get() = layoutNodeWrapper.layoutNode
+
+ private val size: IntSize
+ get() = layoutNodeWrapper.size
+
+ var next: DrawEntity? = null
private var cacheDrawModifier: DrawCacheModifier? = updateCacheDrawModifier()
@@ -38,7 +46,7 @@
override val layoutDirection: LayoutDirection get() = layoutNode.layoutDirection
- override val size: Size get() = measuredSize.toSize()
+ override val size: Size get() = layoutNodeWrapper.size.toSize()
}
// Flag to determine if the cache should be re-built
@@ -71,30 +79,30 @@
}
}
- override fun onInitialize() {
- super.onInitialize()
+ fun onInitialize() {
cacheDrawModifier = updateCacheDrawModifier()
invalidateCache = true
+ next?.onInitialize()
}
- override fun onMeasureResultChanged(width: Int, height: Int) {
- super.onMeasureResultChanged(width, height)
+ fun onMeasureResultChanged(width: Int, height: Int) {
invalidateCache = true
+ next?.onMeasureResultChanged(width, height)
}
// This is not thread safe
- override fun performDraw(canvas: Canvas) {
- val size = measuredSize.toSize()
+ fun draw(canvas: Canvas) {
+ val size = size.toSize()
if (cacheDrawModifier != null && invalidateCache) {
layoutNode.requireOwner().snapshotObserver.observeReads(
this,
- onCommitAffectingModifiedDrawNode,
+ onCommitAffectingDrawEntity,
updateCache
)
}
val drawScope = layoutNode.mDrawScope
- drawScope.draw(canvas, size, wrapped) {
+ drawScope.draw(canvas, size, layoutNodeWrapper, this) {
with(drawScope) {
with(modifier) {
draw()
@@ -107,15 +115,15 @@
// Callback invoked whenever a state parameter that is read within the cache
// execution callback is updated. This marks the cache flag as dirty and
// invalidates the current layer.
- private val onCommitAffectingModifiedDrawNode: (ModifiedDrawNode) -> Unit =
- { modifiedDrawNode ->
- if (modifiedDrawNode.isValid) {
- modifiedDrawNode.invalidateCache = true
- modifiedDrawNode.invalidateLayer()
+ private val onCommitAffectingDrawEntity: (DrawEntity) -> Unit =
+ { drawEntity ->
+ if (drawEntity.isValid) {
+ drawEntity.invalidateCache = true
+ drawEntity.layoutNodeWrapper.invalidateLayer()
}
}
}
override val isValid: Boolean
- get() = isAttached
-}
\ No newline at end of file
+ get() = layoutNodeWrapper.isAttached
+}
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 ab6d3c9..71b3960 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
@@ -653,6 +653,8 @@
val addedCallback = hasNewPositioningCallback()
onPositionedCallbacks?.clear()
+ innerLayoutNodeWrapper.onInitialize()
+
// Create a new chain of LayoutNodeWrappers, reusing existing ones from wrappers
// when possible.
val outerWrapper = modifier.foldOut(innerLayoutNodeWrapper) { mod, toWrap ->
@@ -660,6 +662,13 @@
mod.onRemeasurementAvailable(this)
}
+ if (mod is DrawModifier) {
+ val drawEntity = DrawEntity(toWrap, mod)
+ drawEntity.next = toWrap.drawEntityHead
+ toWrap.drawEntityHead = drawEntity
+ drawEntity.onInitialize()
+ }
+
// Re-use the layoutNodeWrapper if possible.
reuseLayoutNodeWrapper(mod, toWrap)?.let {
return@foldOut it
@@ -686,10 +695,6 @@
.initialize()
.assignChained(toWrap)
}
- if (mod is DrawModifier) {
- wrapper = ModifiedDrawNode(wrapper, mod)
- .initialize()
- }
if (mod is FocusModifier) {
wrapper = ModifiedFocusNode(wrapper, mod)
.initialize()
@@ -1176,8 +1181,23 @@
val infoList = mutableVectorOf<ModifierInfo>()
forEachDelegate { wrapper ->
wrapper as DelegatingLayoutNodeWrapper<*>
- val info = ModifierInfo(wrapper.modifier, wrapper, wrapper.layer)
+ val layer = wrapper.layer
+ val info = ModifierInfo(wrapper.modifier, wrapper, layer)
infoList += info
+ var node = wrapper.drawEntityHead // head
+ while (node != null) {
+ infoList += ModifierInfo(node.modifier, wrapper, layer)
+ node = node.next
+ }
+ }
+ var innerNode = innerLayoutNodeWrapper.drawEntityHead
+ while (innerNode != null) {
+ infoList += ModifierInfo(
+ innerNode.modifier,
+ innerLayoutNodeWrapper,
+ innerLayoutNodeWrapper.layer
+ )
+ innerNode = innerNode.next
}
return infoList.asMutableList()
}
@@ -1243,7 +1263,9 @@
private fun copyWrappersToCache() {
forEachDelegate {
wrapperCache += it as DelegatingLayoutNodeWrapper<*>
+ it.drawEntityHead = null
}
+ innerLayoutNodeWrapper.drawEntityHead = null
}
private fun markReusedModifiers(modifier: Modifier) {
@@ -1330,7 +1352,7 @@
forEachDelegateIncludingInner {
if (it.layer != null) {
return false
- } else if (it is ModifiedDrawNode) {
+ } else if (it.drawEntityHead != null) {
return true
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
index 8493379..de1893f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeDrawScope.kt
@@ -36,27 +36,36 @@
// draw calls for all composables.
// As a result there could be thread safety concerns here for multi-threaded drawing
// scenarios, generally a single ComponentDrawScope should be shared for a particular thread
- private var wrapped: LayoutNodeWrapper? = null
+ private var drawEntity: DrawEntity? = null
override fun drawContent() {
- drawIntoCanvas { canvas -> wrapped?.draw(canvas) }
+ drawIntoCanvas { canvas ->
+ val drawEntity = drawEntity!!
+ val nextDrawEntity = drawEntity.next
+ if (nextDrawEntity != null) {
+ nextDrawEntity.draw(canvas)
+ } else {
+ drawEntity.layoutNodeWrapper.performDraw(canvas)
+ }
+ }
}
internal inline fun draw(
canvas: Canvas,
size: Size,
- LayoutNodeWrapper: LayoutNodeWrapper,
+ layoutNodeWrapper: LayoutNodeWrapper,
+ drawEntity: DrawEntity,
block: DrawScope.() -> Unit
) {
- val previousWrapper = wrapped
- wrapped = LayoutNodeWrapper
+ val previousDrawEntity = this.drawEntity
+ this.drawEntity = drawEntity
canvasDrawScope.draw(
- LayoutNodeWrapper.measureScope,
- LayoutNodeWrapper.measureScope.layoutDirection,
+ layoutNodeWrapper.measureScope,
+ layoutNodeWrapper.measureScope.layoutDirection,
canvas,
size,
block
)
- wrapped = previousWrapper
+ this.drawEntity = previousDrawEntity
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
index 7f82f7b..a240c7d 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
@@ -154,6 +154,7 @@
}
layoutNode.owner?.onLayoutChange(layoutNode)
measuredSize = IntSize(width, height)
+ drawEntityHead?.onMeasureResultChanged(width, height)
}
var position: IntOffset = IntOffset.Zero
@@ -190,6 +191,11 @@
private val snapshotObserver get() = layoutNode.requireOwner().snapshotObserver
+ /**
+ * The head of the DrawEntity linked list
+ */
+ var drawEntityHead: DrawEntity? = null
+
protected inline fun performingMeasure(
constraints: Constraints,
block: () -> Placeable
@@ -214,6 +220,14 @@
}
/**
+ * An initialization function that is called when the [LayoutNodeWrapper] is initially created,
+ * and also called when the [LayoutNodeWrapper] is re-used.
+ */
+ open fun onInitialize() {
+ layer?.invalidate()
+ }
+
+ /**
* Places the modified child.
*/
/*@CallSuper*/
@@ -252,12 +266,23 @@
val x = position.x.toFloat()
val y = position.y.toFloat()
canvas.translate(x, y)
- performDraw(canvas)
+ drawContainedDrawModifiers(canvas)
canvas.translate(-x, -y)
}
}
- protected abstract fun performDraw(canvas: Canvas)
+ private fun drawContainedDrawModifiers(canvas: Canvas) {
+ val head = drawEntityHead
+ if (head == null) {
+ performDraw(canvas)
+ } else {
+ head.draw(canvas)
+ }
+ }
+
+ open fun performDraw(canvas: Canvas) {
+ wrapped?.draw(canvas)
+ }
open fun onPlaced() {}
@@ -266,7 +291,7 @@
override fun invoke(canvas: Canvas) {
if (layoutNode.isPlaced) {
snapshotObserver.observeReads(this, onCommitAffectingLayer) {
- performDraw(canvas)
+ drawContainedDrawModifiers(canvas)
}
lastLayerDrawingWasSkipped = false
} else {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
index 1bc1644..775bdd3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
@@ -256,6 +256,9 @@
/**
* Makes sure the passed [layoutNode] and its subtree is remeasured and has the final sizes.
+ *
+ * The node or some of the nodes in its subtree can still be kept unmeasured if they are
+ * not placed and don't affect the parent size. See [requestRemeasure] for details.
*/
fun forceMeasureTheSubtree(layoutNode: LayoutNode) {
// if there is nothing in `relayoutNodes` everything is remeasured.
@@ -273,8 +276,13 @@
remeasureAndRelayoutIfNeeded(child)
}
- // run recursively for the subtree.
- forceMeasureTheSubtree(child)
+ // if the child is still in NeedsRemeasure state then this child remeasure wasn't
+ // needed. it can happen for example when this child is not placed and can't affect
+ // the parent size. we can skip the whole subtree.
+ if (child.layoutState != NeedsRemeasure) {
+ // run recursively for the subtree.
+ forceMeasureTheSubtree(child)
+ }
}
// if the child was resized during the remeasurement it could request a remeasure on
@@ -283,8 +291,6 @@
if (layoutNode.layoutState == NeedsRemeasure && relayoutNodes.remove(layoutNode)) {
remeasureAndRelayoutIfNeeded(layoutNode)
}
-
- require(layoutNode.layoutState != NeedsRemeasure)
}
/**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierLocalConsumerNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierLocalConsumerNode.kt
index 8966754..6ab7e29 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierLocalConsumerNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/ModifierLocalConsumerNode.kt
@@ -27,7 +27,7 @@
override fun onModifierChanged() {
super.onModifierChanged()
- if (isAttached) notifyConsumerOfChanges()
+ notifyConsumerOfChanges()
}
override fun attach() {
@@ -39,6 +39,10 @@
get() = onModifierLocalRead(this)
private fun notifyConsumerOfChanges() {
+ // If the node is not attached, we don't notify the consumers.
+ // Ultimately when the node is attached, this function will be called again.
+ if (!isAttached) return
+
layoutNode.requireOwner().snapshotObserver.observeReads(this, onReadValuesChanged) {
modifier.onModifierLocalsUpdated(this)
}
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/ComposeScene.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/ComposeScene.desktop.kt
index 5ecc568..2a64b96 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/ComposeScene.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/ComposeScene.desktop.kt
@@ -47,7 +47,8 @@
nativeEvent: Any?,
type: PointerType,
isMousePressed: Boolean,
- pointerId: Long
+ pointerId: Long,
+ scrollDelta: Offset
): PointerInputEvent {
return PointerInputEvent(
eventType,
@@ -59,7 +60,8 @@
position,
position,
isMousePressed,
- type
+ type,
+ scrollDelta = scrollDelta
)
),
nativeEvent as MouseEvent?
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeLayer.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeLayer.desktop.kt
index cd9aaf9..08c470d 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeLayer.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/awt/ComposeLayer.desktop.kt
@@ -19,8 +19,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.input.mouse.MouseScrollOrientation
-import androidx.compose.ui.input.mouse.MouseScrollUnit
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.PointerType
import androidx.compose.ui.platform.PlatformComponent
@@ -267,19 +265,14 @@
private fun ComposeScene.onMouseWheelEvent(
density: Float,
event: MouseWheelEvent
-) = with(event) {
- sendPointerScrollEvent(
+) {
+ sendPointerEvent(
+ eventType = PointerEventType.Scroll,
position = Offset(event.x.toFloat(), event.y.toFloat()) * density,
- delta = if (scrollType == MouseWheelEvent.WHEEL_BLOCK_SCROLL) {
- MouseScrollUnit.Page((scrollAmount * preciseWheelRotation).toFloat())
+ scrollDelta = if (event.isShiftDown) {
+ Offset(event.preciseWheelRotation.toFloat(), 0f)
} else {
- MouseScrollUnit.Line((scrollAmount * preciseWheelRotation).toFloat())
- },
- // There are no other way to detect horizontal scrolling in AWT
- orientation = if (isShiftDown) {
- MouseScrollOrientation.Horizontal
- } else {
- MouseScrollOrientation.Vertical
+ Offset(0f, event.preciseWheelRotation.toFloat())
},
timeMillis = event.`when`,
type = PointerType.Mouse,
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.desktop.kt
index 309be44..2c74cda 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/input/pointer/PointerEvent.desktop.kt
@@ -46,7 +46,10 @@
internal actual constructor(
changes: List<PointerInputChange>,
internalPointerEvent: InternalPointerEvent?
- ) : this(changes, internalPointerEvent?.mouseEvent) {
+ ) : this(
+ changes,
+ internalPointerEvent?.mouseEvent
+ ) {
this.type = internalPointerEvent?.type ?: PointerEventType.Unknown
}
@@ -57,7 +60,10 @@
/**
* @param changes The changes.
*/
- actual constructor(changes: List<PointerInputChange>) : this(changes, mouseEvent = null)
+ actual constructor(changes: List<PointerInputChange>) : this(
+ changes,
+ mouseEvent = null
+ )
actual var type: PointerEventType = PointerEventType.Unknown
internal set
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/TestComposeWindow.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/TestComposeWindow.desktop.kt
index 4eb942b..fc04147 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/TestComposeWindow.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/TestComposeWindow.desktop.kt
@@ -24,6 +24,8 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.mouse.MouseScrollEvent
+import androidx.compose.ui.input.mouse.MouseScrollOrientation
+import androidx.compose.ui.input.mouse.MouseScrollUnit
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.node.RootForTest
import androidx.compose.ui.unit.Constraints
@@ -34,7 +36,11 @@
import org.jetbrains.skia.Surface
import org.jetbrains.skiko.FrameDispatcher
import java.awt.Component
+import java.awt.event.MouseWheelEvent
import kotlin.coroutines.CoroutineContext
+import kotlin.math.abs
+import kotlin.math.roundToInt
+import kotlin.math.sign
/**
* A virtual window for testing purposes.
@@ -115,10 +121,36 @@
*/
@OptIn(ExperimentalComposeUiApi::class)
fun onMouseScroll(x: Int, y: Int, event: MouseScrollEvent) {
- scene.sendPointerScrollEvent(
+ val delta = when (event.delta) {
+ is MouseScrollUnit.Line -> event.delta.value
+ is MouseScrollUnit.Page -> event.delta.value
+ }
+ val wheelRotation = sign(delta)
+ scene.sendPointerEvent(
+ eventType = PointerEventType.Scroll,
position = Offset(x.toFloat(), y.toFloat()),
- delta = event.delta,
- orientation = event.orientation
+ scrollDelta = if (event.orientation == MouseScrollOrientation.Vertical) {
+ Offset(0f, wheelRotation)
+ } else {
+ Offset(wheelRotation, 0f)
+ },
+ nativeEvent = MouseWheelEvent(
+ EventComponent,
+ MouseWheelEvent.MOUSE_WHEEL,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ false,
+ if (event.delta is MouseScrollUnit.Line) {
+ MouseWheelEvent.WHEEL_UNIT_SCROLL
+ } else {
+ MouseWheelEvent.WHEEL_BLOCK_SCROLL
+ },
+ abs(delta.roundToInt()),
+ wheelRotation.roundToInt()
+ )
)
}
diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/ComposeScene.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/ComposeScene.skiko.kt
index f3a83bd..c358016 100644
--- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/ComposeScene.skiko.kt
+++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/ComposeScene.skiko.kt
@@ -28,9 +28,6 @@
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.key.KeyEvent as ComposeKeyEvent
-import androidx.compose.ui.input.mouse.MouseScrollEvent
-import androidx.compose.ui.input.mouse.MouseScrollOrientation
-import androidx.compose.ui.input.mouse.MouseScrollUnit
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.PointerInputEvent
import androidx.compose.ui.input.pointer.PointerType
@@ -346,6 +343,7 @@
*
* @param eventType Indicates the primary reason that the event was sent.
* @param position The [Offset] of the current pointer event, relative to the content.
+ * @param scrollDelta scroll delta for the PointerEventType.Scroll event
* @param timeMillis The time of the current pointer event, in milliseconds. The start (`0`) time
* is platform-dependent.
* @param type The device type that produced the event, such as [mouse][PointerType.Mouse],
@@ -356,6 +354,7 @@
fun sendPointerEvent(
eventType: PointerEventType,
position: Offset,
+ scrollDelta: Offset = Offset(0f, 0f),
timeMillis: Long = System.nanoTime() / 1_000_000L,
type: PointerType = PointerType.Mouse,
nativeEvent: Any? = null,
@@ -369,7 +368,14 @@
PointerEventType.Release -> isMousePressed = false
}
val event = pointerInputEvent(
- eventType, position, timeMillis, nativeEvent, type, isMousePressed, pointerId
+ eventType,
+ position,
+ timeMillis,
+ nativeEvent,
+ type,
+ isMousePressed,
+ pointerId,
+ scrollDelta
)
when (eventType) {
PointerEventType.Press -> onMousePressed(event)
@@ -380,56 +386,10 @@
}
PointerEventType.Enter -> hoveredOwner?.processPointerInput(event)
PointerEventType.Exit -> hoveredOwner?.processPointerInput(event)
+ PointerEventType.Scroll -> hoveredOwner?.processPointerInput(event)
}
}
- // TODO(demin): remove/change when we will have scroll event support in the common code
- // TODO(demin): return Boolean (when it is consumed).
- // see ComposeLayer todo about AWTDebounceEventQueue
- /**
- * Send pointer scroll event to the content.
- *
- * @param position The [Offset] of the current pointer event, relative to the content
- * @param delta Change of mouse scroll.
- * Positive if scrolling down, negative if scrolling up.
- * @param orientation Orientation in which scrolling event occurs.
- * Up/down wheel scrolling causes events in vertical orientation.
- * Left/right wheel scrolling causes events in horizontal orientation.
- * @param timeMillis The time of the current pointer event, in milliseconds. The start (`0`) time
- * is platform-dependent.
- * @param type The device type that produced the event, such as [mouse][PointerType.Mouse],
- * or [touch][PointerType.Touch].
- * @param nativeEvent The original native event
- */
- @OptIn(ExperimentalComposeUiApi::class)
- @Suppress("UNUSED_PARAMETER")
- @ExperimentalComposeUiApi // it is more experimental than ComposeScene itself
- fun sendPointerScrollEvent(
- position: Offset,
- delta: MouseScrollUnit,
- orientation: MouseScrollOrientation = MouseScrollOrientation.Vertical,
- timeMillis: Long = System.nanoTime() / 1_000_000L,
- type: PointerType = PointerType.Mouse,
- nativeEvent: Any? = null,
-// buttons: PointerButtons? = null,
-// keyboardModifiers: PointerKeyboardModifiers? = null,
- ): Unit = postponeInvalidation {
- check(!isDisposed) { "ComposeScene is disposed" }
- hoveredOwner?.onMouseScroll(
- position,
- MouseScrollEvent(delta, orientation),
- pointerInputEvent = pointerInputEvent(
- PointerEventType.Unknown,
- position,
- timeMillis,
- nativeEvent,
- type,
- isMousePressed,
- pointerId
- )
- )
- }
-
private fun onMousePressed(event: PointerInputEvent) {
val currentOwner = hoveredOwner
if (currentOwner != null) {
@@ -471,5 +431,6 @@
nativeEvent: Any?,
type: PointerType,
isMousePressed: Boolean,
- pointerId: Long
+ pointerId: Long,
+ scrollDelta: Offset
): PointerInputEvent
diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilter.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilter.skiko.kt
index 4cce567..cf9d2a0 100644
--- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilter.skiko.kt
+++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/input/mouse/MouseScrollFilter.skiko.kt
@@ -18,15 +18,14 @@
package androidx.compose.ui.input.mouse
-import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
-import androidx.compose.ui.input.pointer.PointerInputModifier
-import androidx.compose.ui.input.pointer.PointerEvent
-import androidx.compose.ui.input.pointer.PointerEventPass
-import androidx.compose.ui.input.pointer.PointerInputFilter
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEventType
+import androidx.compose.ui.input.pointer.PointerType
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.IntSize
+import java.awt.event.MouseWheelEvent
/**
* Indicates distance by which we should scroll some container.
@@ -78,6 +77,10 @@
val orientation: MouseScrollOrientation
)
+// TODO(demin): how easy-to-use scroll API should look like?
+// maybe something like Modifier.pointerScroll { delta: Offset -> } ?
+// or Modifier.pointerInput(Unit) { scroll { delta: Offset -> } }
+// ?
/**
* Adding this [modifier][Modifier] to the [modifier][Modifier] parameter of a component will
* allow it to intercept scroll events from mouse wheel and touchpad.
@@ -100,31 +103,40 @@
*/
bounds: IntSize
) -> Boolean
-): Modifier = composed {
- val filter = remember(::MouseScrollEventFilter)
- filter.onMouseScroll = onMouseScroll
- MousePointerInputModifierImpl(filter)
-}
-
-internal class MouseScrollEventFilter : PointerInputFilter() {
- lateinit var onMouseScroll: (MouseScrollEvent, IntSize) -> Boolean
-
- override fun onPointerEvent(
- pointerEvent: PointerEvent,
- pass: PointerEventPass,
- bounds: IntSize
- ) = Unit
-
- override fun onCancel() = Unit
-
- fun onMouseScroll(event: MouseScrollEvent): Boolean {
- return isAttached && onMouseScroll(event, size)
+): Modifier = pointerInput(onMouseScroll) {
+ awaitPointerEventScope {
+ while (true) {
+ val event = awaitPointerEvent()
+ val mouseEvent = (event.mouseEvent as? MouseWheelEvent) ?: continue
+ val mouseChange = event.changes.find { it.type == PointerType.Mouse }
+ val isScroll = event.type == PointerEventType.Scroll
+ if (isScroll && mouseChange != null && !mouseChange.isConsumed) {
+ val legacyEvent = mouseEvent.toLegacyEvent(mouseChange.scrollDelta)
+ if (onMouseScroll(legacyEvent, size)) {
+ mouseChange.consume()
+ }
+ }
+ }
}
}
-private data class MousePointerInputModifierImpl(
- override val pointerInputFilter: PointerInputFilter
-) : PointerInputModifier
+private fun MouseWheelEvent.toLegacyEvent(scrollDelta: Offset): MouseScrollEvent {
+ val value = if (scrollDelta.x != 0f) scrollDelta.x else scrollDelta.y
+ return MouseScrollEvent(
+ delta = if (scrollType == MouseWheelEvent.WHEEL_BLOCK_SCROLL) {
+ MouseScrollUnit.Page(value * scrollAmount)
+ } else {
+ MouseScrollUnit.Line(value * scrollAmount)
+ },
+
+ // There are no other way to detect horizontal scrolling in AWT
+ orientation = if (isShiftDown || scrollDelta.x != 0f) {
+ MouseScrollOrientation.Horizontal
+ } else {
+ MouseScrollOrientation.Vertical
+ }
+ )
+}
@ExperimentalComposeUiApi
enum class MouseScrollOrientation {
diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaBasedOwner.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaBasedOwner.skiko.kt
index a3d80e6..9f68c30 100644
--- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaBasedOwner.skiko.kt
+++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaBasedOwner.skiko.kt
@@ -47,20 +47,16 @@
import androidx.compose.ui.input.key.isShiftPressed
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
-import androidx.compose.ui.input.mouse.MouseScrollEvent
-import androidx.compose.ui.input.mouse.MouseScrollEventFilter
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.PointerIconDefaults
import androidx.compose.ui.input.pointer.PointerIconService
import androidx.compose.ui.input.pointer.PointerInputEvent
import androidx.compose.ui.input.pointer.PointerInputEventProcessor
-import androidx.compose.ui.input.pointer.PointerInputFilter
import androidx.compose.ui.input.pointer.PositionCalculator
import androidx.compose.ui.input.pointer.ProcessResult
import androidx.compose.ui.input.pointer.TestPointerInputEventData
import androidx.compose.ui.layout.RootMeasurePolicy
-import androidx.compose.ui.node.HitTestResult
import androidx.compose.ui.node.InternalCoreApi
import androidx.compose.ui.node.LayoutNode
import androidx.compose.ui.node.LayoutNodeDrawScope
@@ -405,31 +401,6 @@
)
}
- // TODO(demin): This is likely temporary. After PointerInputEvent can handle mouse events
- // (scroll in particular), we can replace it with processPointerInput. see b/166105940
- internal fun onMouseScroll(
- position: Offset,
- event: MouseScrollEvent,
- pointerInputEvent: PointerInputEvent
- ) {
- measureAndLayout()
- sendSyntheticEvents()
- lastPointerEvent = pointerInputEvent
-
- val inputFilters = HitTestResult<PointerInputFilter>()
- root.hitTest(position, inputFilters)
-
- for (
- filter in inputFilters
- .asReversed()
- .asSequence()
- .filterIsInstance<MouseScrollEventFilter>()
- ) {
- val isConsumed = filter.onMouseScroll(event)
- if (isConsumed) break
- }
- }
-
override val pointerIconService: PointerIconService =
object : PointerIconService {
override var current: PointerIcon
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt
index 0ee1040..07b9430 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/focus/FocusManagerTest.kt
@@ -30,7 +30,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-import kotlin.jvm.JvmStatic
@RunWith(Parameterized::class)
class FocusManagerTest(private val initialFocusState: FocusState) {
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/pointer/PointerInputTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/pointer/PointerInputTest.kt
index 353cc36..d2ca364 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/pointer/PointerInputTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/pointer/PointerInputTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.ui.input.pointer
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.geometry.Offset
import com.google.common.truth.FailureMetadata
import com.google.common.truth.Subject
@@ -347,6 +348,16 @@
assertThat(pointerInputChange.anyChangeConsumed()).isFalse()
}
+ @OptIn(ExperimentalComposeUiApi::class)
+ @Test
+ fun consume_noChanges_returnsTrue() {
+ val pointerInputChange =
+ createPointerInputChange(8f, 16f, true, 8f, 16f, true, 0f, 0f, false)
+ pointerInputChange.consume()
+ assertThat(pointerInputChange.isConsumed).isTrue()
+ assertThat(pointerInputChange.anyChangeConsumed()).isTrue()
+ }
+
@Test
fun anyChangeConsumed_downConsumed_returnsTrue() {
val pointerInputChange =
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
index 7475387..6c5a986 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/LayoutNodeTest.kt
@@ -681,8 +681,8 @@
@Test
fun coordinatesAttachedWhenLayoutNodeAttached() {
val layoutNode = LayoutNode()
- val drawModifier = Modifier.drawBehind { }
- layoutNode.modifier = drawModifier
+ val layoutModifier = Modifier.graphicsLayer { }
+ layoutNode.modifier = layoutModifier
assertFalse(layoutNode.coordinates.isAttached)
assertFalse(layoutNode.coordinates.isAttached)
layoutNode.attach(MockOwner())
@@ -697,16 +697,16 @@
@Test
fun layoutNodeWrapperSameWithReplacementModifier() {
val layoutNode = LayoutNode()
- val drawModifier = Modifier.drawBehind { }
+ val layoutModifier = Modifier.graphicsLayer { }
- layoutNode.modifier = drawModifier
+ layoutNode.modifier = layoutModifier
val oldLayoutNodeWrapper = layoutNode.outerLayoutNodeWrapper
assertFalse(oldLayoutNodeWrapper.isAttached)
layoutNode.attach(MockOwner())
assertTrue(oldLayoutNodeWrapper.isAttached)
- layoutNode.modifier = Modifier.drawBehind { }
+ layoutNode.modifier = Modifier.graphicsLayer { }
val newLayoutNodeWrapper = layoutNode.outerLayoutNodeWrapper
assertSame(newLayoutNodeWrapper, oldLayoutNodeWrapper)
}
@@ -741,9 +741,9 @@
@Test
fun layoutNodeWrapperAttachedWhenLayoutNodeAttached() {
val layoutNode = LayoutNode()
- val drawModifier = Modifier.drawBehind { }
+ val layoutModifier = Modifier.graphicsLayer { }
- layoutNode.modifier = drawModifier
+ layoutNode.modifier = layoutModifier
val oldLayoutNodeWrapper = layoutNode.outerLayoutNodeWrapper
assertFalse(oldLayoutNodeWrapper.isAttached)
@@ -760,8 +760,8 @@
fun layoutNodeWrapperParentLayoutCoordinates() {
val layoutNode = LayoutNode()
val layoutNode2 = LayoutNode()
- val drawModifier = Modifier.drawBehind { }
- layoutNode.modifier = drawModifier
+ val layoutModifier = Modifier.graphicsLayer { }
+ layoutNode.modifier = layoutModifier
layoutNode2.insertAt(0, layoutNode)
layoutNode2.attach(MockOwner())
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierLocalConsumerNodeTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierLocalConsumerNodeTest.kt
new file mode 100644
index 0000000..4cc4663
--- /dev/null
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/node/ModifierLocalConsumerNodeTest.kt
@@ -0,0 +1,358 @@
+/*
+ * 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.compose.ui.node
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.autofill.Autofill
+import androidx.compose.ui.autofill.AutofillTree
+import androidx.compose.ui.focus.FocusManager
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Canvas
+import androidx.compose.ui.hapticfeedback.HapticFeedback
+import androidx.compose.ui.input.InputModeManager
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.pointer.PointerIconService
+import androidx.compose.ui.modifier.modifierLocalConsumer
+import androidx.compose.ui.modifier.modifierLocalOf
+import androidx.compose.ui.modifier.modifierLocalProvider
+import androidx.compose.ui.platform.AccessibilityManager
+import androidx.compose.ui.platform.ClipboardManager
+import androidx.compose.ui.platform.TextToolbar
+import androidx.compose.ui.platform.ViewConfiguration
+import androidx.compose.ui.platform.WindowInfo
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.input.TextInputService
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalComposeUiApi::class)
+@RunWith(JUnit4::class)
+class ModifierLocalConsumerNodeTest {
+
+ private val default = "Default"
+ private val ModifierLocalString = modifierLocalOf { "Default" }
+
+ private val owner = FakeOwner()
+ private val layoutNode = LayoutNode()
+
+ @Test
+ fun `unattached modifier local consumer does not invoke lambda`() {
+ // Arrange.
+ var receivedValue = ""
+ TestBox(Modifier.modifierLocalConsumer { receivedValue = ModifierLocalString.current })
+
+ // Assert.
+ assertThat(receivedValue).isEmpty()
+ }
+
+ @Test
+ fun `attached modifier local consumer with no provider reads default value`() {
+ // Arrange.
+ lateinit var receivedValue: String
+ TestBox(Modifier.modifierLocalConsumer { receivedValue = ModifierLocalString.current })
+
+ // Act.
+ attach()
+
+ // Assert.
+ assertThat(receivedValue).isEqualTo(default)
+ }
+
+ @Test
+ fun `changing the consumer modifier with no provider reads default value`() {
+ // Arrange.
+ lateinit var receivedValue: String
+ TestBox(Modifier.modifierLocalConsumer { receivedValue = ModifierLocalString.current })
+ attach()
+ receivedValue = ""
+
+ // Act.
+ changeModifier(Modifier.modifierLocalConsumer {
+ receivedValue = ModifierLocalString.current
+ })
+
+ // Assert.
+ assertThat(receivedValue).isEqualTo(default)
+ }
+
+ @Test
+ fun `detached modifier local consumer with no provider does not invoke lambda`() {
+ // Arrange.
+ lateinit var receivedValue: String
+ TestBox(Modifier.modifierLocalConsumer { receivedValue = ModifierLocalString.current })
+ attach()
+ receivedValue = ""
+
+ // Act.
+ detach()
+
+ // Assert.
+ assertThat(receivedValue).isEmpty()
+ }
+
+ @Test
+ fun `unattached modifier local consumer with provider does not invoke lambda`() {
+ // Arrange.
+ var receivedValue = ""
+ TestBox(
+ modifier = Modifier
+ .modifierLocalProvider(ModifierLocalString) { "Initial Value" }
+ .modifierLocalConsumer { receivedValue = ModifierLocalString.current }
+ )
+
+ // Assert.
+ assertThat(receivedValue).isEmpty()
+ }
+
+ @Test
+ fun `attached modifier local consumer with provider reads provided value`() {
+ // Arrange.
+ val providedValue = "Provided Value"
+ lateinit var receivedValue: String
+ TestBox(
+ modifier = Modifier
+ .modifierLocalProvider(ModifierLocalString) { providedValue }
+ .modifierLocalConsumer { receivedValue = ModifierLocalString.current }
+ )
+ // Act.
+ attach()
+
+ // Assert.
+ assertThat(receivedValue).isEqualTo(providedValue)
+ }
+
+ @Test
+ fun `changing provided value causes consumer to receive new provided value`() {
+ // Arrange.
+ val initialValue = "Initial Value"
+ val finalValue = "Final Value"
+ var providedValue by mutableStateOf(initialValue)
+ lateinit var receivedValue: String
+ TestBox(
+ modifier = Modifier
+ .modifierLocalProvider(ModifierLocalString) { providedValue }
+ .modifierLocalConsumer { receivedValue = ModifierLocalString.current }
+ )
+ attach()
+
+ // Act.
+ Snapshot.withMutableSnapshot {
+ providedValue = finalValue
+ }
+
+ // Assert.
+ assertThat(receivedValue).isEqualTo(finalValue)
+ }
+
+ @Test
+ fun `changing provided value after detaching modifier does not invoke consumer lambda`() {
+ // Arrange.
+ val initialValue = "Initial Value"
+ val finalValue = "Final Value"
+ var providedValue by mutableStateOf(initialValue)
+ lateinit var receivedValue: String
+ TestBox(
+ modifier = Modifier
+ .modifierLocalProvider(ModifierLocalString) { providedValue }
+ .modifierLocalConsumer { receivedValue = ModifierLocalString.current }
+ )
+ attach()
+ detach()
+ receivedValue = ""
+
+ // Act.
+ Snapshot.withMutableSnapshot {
+ providedValue = finalValue
+ }
+
+ // Assert.
+ assertThat(receivedValue).isEmpty()
+ }
+
+ @Test
+ fun `changing modifiers after detaching modifier does not invoke consumer lambda`() {
+ // Arrange.
+ lateinit var receivedValue: String
+ TestBox(
+ modifier = Modifier
+ .modifierLocalProvider(ModifierLocalString) { "Provided Value" }
+ .modifierLocalConsumer { receivedValue = ModifierLocalString.current }
+ )
+ attach()
+ detach()
+ receivedValue = ""
+
+ // Act.
+ changeModifier(Modifier.modifierLocalConsumer {
+ receivedValue = ModifierLocalString.current
+ })
+
+ // Assert.
+ assertThat(receivedValue).isEmpty()
+ }
+
+ @Test
+ fun `changing the consumer modifier with provider reads provided value`() {
+ // Arrange.
+ val providedValue = "Provided Value"
+ lateinit var receivedValue: String
+ TestBox(
+ modifier = Modifier
+ .modifierLocalProvider(ModifierLocalString) { providedValue }
+ .modifierLocalConsumer { receivedValue = ModifierLocalString.current }
+ )
+ attach()
+ receivedValue = ""
+
+ // Act.
+ changeModifier(
+ Modifier
+ .modifierLocalProvider(ModifierLocalString) { providedValue }
+ .modifierLocalConsumer { receivedValue = ModifierLocalString.current }
+ )
+
+ // Assert.
+ assertThat(receivedValue).isEqualTo(providedValue)
+ }
+
+ @Test
+ fun `detached modifier local consumer with provider does not invoke lambda`() {
+ // Arrange.
+ lateinit var receivedValue: String
+ TestBox(
+ modifier = Modifier
+ .modifierLocalProvider(ModifierLocalString) { "Provided Value" }
+ .modifierLocalConsumer { receivedValue = ModifierLocalString.current }
+ )
+ attach()
+ receivedValue = ""
+
+ // Act.
+ detach()
+
+ // Assert.
+ assertThat(receivedValue).isEmpty()
+ }
+
+ private fun TestBox(modifier: Modifier = Modifier) {
+ owner.snapshotObserver.startObserving()
+ layoutNode.modifier = modifier
+ }
+
+ private fun attach() {
+ layoutNode.attach(owner)
+ }
+
+ private fun detach() {
+ layoutNode.detach()
+ }
+
+ private fun changeModifier(modifier: Modifier) {
+ with(layoutNode) {
+ if (isAttached) { forEachLayoutNodeWrapper { it.detach() } }
+ this.modifier = modifier
+ if (isAttached) { forEachLayoutNodeWrapper { it.attach() } }
+ }
+ }
+
+ @OptIn(ExperimentalComposeUiApi::class)
+ private class FakeOwner : Owner {
+ @OptIn(InternalCoreApi::class)
+ override var showLayoutBounds: Boolean = false
+ override val snapshotObserver: OwnerSnapshotObserver = OwnerSnapshotObserver { it.invoke() }
+ override fun onRequestMeasure(layoutNode: LayoutNode) {}
+ override fun onAttach(node: LayoutNode) = node.forEachLayoutNodeWrapper { it.attach() }
+ override fun onDetach(node: LayoutNode) = node.forEachLayoutNodeWrapper { it.detach() }
+
+ override val root: LayoutNode
+ get() = TODO("Not yet implemented")
+ override val sharedDrawScope: LayoutNodeDrawScope
+ get() = TODO("Not yet implemented")
+ override val rootForTest: RootForTest
+ get() = TODO("Not yet implemented")
+ override val hapticFeedBack: HapticFeedback
+ get() = TODO("Not yet implemented")
+ override val inputModeManager: InputModeManager
+ get() = TODO("Not yet implemented")
+ override val clipboardManager: ClipboardManager
+ get() = TODO("Not yet implemented")
+ override val accessibilityManager: AccessibilityManager
+ get() = TODO("Not yet implemented")
+ override val textToolbar: TextToolbar
+ get() = TODO("Not yet implemented")
+ override val density: Density
+ get() = TODO("Not yet implemented")
+ override val textInputService: TextInputService
+ get() = TODO("Not yet implemented")
+ override val pointerIconService: PointerIconService
+ get() = TODO("Not yet implemented")
+ override val focusManager: FocusManager
+ get() = TODO("Not yet implemented")
+ override val windowInfo: WindowInfo
+ get() = TODO("Not yet implemented")
+ override val fontLoader: Font.ResourceLoader
+ get() = TODO("Not yet implemented")
+ override val layoutDirection: LayoutDirection
+ get() = TODO("Not yet implemented")
+ override val measureIteration: Long
+ get() = TODO("Not yet implemented")
+ override val viewConfiguration: ViewConfiguration
+ get() = TODO("Not yet implemented")
+ override val autofillTree: AutofillTree
+ get() = TODO("Not yet implemented")
+ override val autofill: Autofill
+ get() = TODO("Not yet implemented")
+
+ override fun createLayer(drawBlock: (Canvas) -> Unit, invalidateParentLayer: () -> Unit) =
+ TODO("Not yet implemented")
+ override fun onRequestRelayout(layoutNode: LayoutNode) =
+ TODO("Not yet implemented")
+ override fun calculatePositionInWindow(localPosition: Offset) =
+ TODO("Not yet implemented")
+ override fun calculateLocalPosition(positionInWindow: Offset) =
+ TODO("Not yet implemented")
+ override fun requestFocus() =
+ TODO("Not yet implemented")
+ override fun measureAndLayout(sendPointerUpdate: Boolean) =
+ TODO("Not yet implemented")
+ override fun forceMeasureTheSubtree(layoutNode: LayoutNode) =
+ TODO("Not yet implemented")
+ override fun onSemanticsChange() =
+ TODO("Not yet implemented")
+ override fun onLayoutChange(layoutNode: LayoutNode) =
+ TODO("Not yet implemented")
+ override fun getFocusDirection(keyEvent: KeyEvent) =
+ TODO("Not yet implemented")
+ }
+}
+
+private fun LayoutNode.forEachLayoutNodeWrapper(action: (LayoutNodeWrapper) -> Unit) {
+ var layoutNodeWrapper: LayoutNodeWrapper? = outerLayoutNodeWrapper
+ while (layoutNodeWrapper != null) {
+ action.invoke(layoutNodeWrapper)
+ layoutNodeWrapper = layoutNodeWrapper.wrapped
+ }
+}
diff --git a/coordinatorlayout/coordinatorlayout/build.gradle b/coordinatorlayout/coordinatorlayout/build.gradle
index 569b8bf..e883702 100644
--- a/coordinatorlayout/coordinatorlayout/build.gradle
+++ b/coordinatorlayout/coordinatorlayout/build.gradle
@@ -8,7 +8,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api("androidx.core:core:1.3.0-beta01")
+ api("androidx.core:core:1.3.0")
implementation("androidx.collection:collection:1.0.0")
api("androidx.customview:customview:1.0.0")
diff --git a/core/OWNERS b/core/OWNERS
index cb02012..3a3ca6d 100644
--- a/core/OWNERS
+++ b/core/OWNERS
@@ -16,5 +16,5 @@
siyamed@google.com
# For shortcut related files
-mett@google.com
+sunnygoyal@google.com
pinyaoting@google.com
diff --git a/core/core-google-shortcuts/build.gradle b/core/core-google-shortcuts/build.gradle
index e27488b..1931126 100644
--- a/core/core-google-shortcuts/build.gradle
+++ b/core/core-google-shortcuts/build.gradle
@@ -31,10 +31,12 @@
}
dependencies {
- api("androidx.core:core:1.6.0-rc01")
+ api("androidx.core:core:1.7.0")
implementation("com.google.firebase:firebase-appindexing:20.0.0")
implementation("com.google.crypto.tink:tink-android:1.5.0")
+ implementation project(":appsearch:appsearch")
+ implementation project(":appsearch:appsearch-builtin-types")
androidTestImplementation(libs.testExtJunit)
androidTestImplementation(libs.testCore)
@@ -46,6 +48,7 @@
androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
androidTestImplementation(libs.truth)
androidTestImplementation(libs.multidex)
+ androidTestImplementation("junit:junit:4.13")
}
androidx {
diff --git a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java
index 63d6a76..70c3bf5 100644
--- a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java
+++ b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java
@@ -30,6 +30,7 @@
import androidx.core.google.shortcuts.builders.CapabilityBuilder;
import androidx.core.google.shortcuts.builders.ParameterBuilder;
import androidx.core.google.shortcuts.builders.ShortcutBuilder;
+import androidx.core.google.shortcuts.utils.ShortcutUtils;
import androidx.core.graphics.drawable.IconCompat;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
diff --git a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/TrampolineActivityTest.java b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/TrampolineActivityTest.java
index 8705d6f..b14761a 100644
--- a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/TrampolineActivityTest.java
+++ b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/TrampolineActivityTest.java
@@ -16,8 +16,8 @@
package androidx.core.google.shortcuts;
-import static androidx.core.google.shortcuts.ShortcutUtils.SHORTCUT_TAG_KEY;
-import static androidx.core.google.shortcuts.ShortcutUtils.SHORTCUT_URL_KEY;
+import static androidx.core.google.shortcuts.utils.ShortcutUtils.SHORTCUT_TAG_KEY;
+import static androidx.core.google.shortcuts.utils.ShortcutUtils.SHORTCUT_URL_KEY;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.Intents.times;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
@@ -31,6 +31,7 @@
import android.content.pm.ResolveInfo;
import androidx.core.google.shortcuts.test.TestActivity;
+import androidx.core.google.shortcuts.utils.ShortcutUtils;
import androidx.test.core.app.ActivityScenario;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.espresso.intent.Intents;
diff --git a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/converters/AppSearchDocumentConverterFactoryTest.java b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/converters/AppSearchDocumentConverterFactoryTest.java
new file mode 100644
index 0000000..b11e60c
--- /dev/null
+++ b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/converters/AppSearchDocumentConverterFactoryTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.core.google.shortcuts.converters;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class AppSearchDocumentConverterFactoryTest {
+ @Test
+ @SmallTest
+ public void testGetConverter_registeredConverter_returnsRegisteredConverter() {
+ AppSearchDocumentConverter converter = AppSearchDocumentConverterFactory
+ .getConverter("Timer");
+
+ assertThat(converter).isNotNull();
+ assertThat(converter).isInstanceOf(TimerConverter.class);
+ }
+
+ @Test
+ @SmallTest
+ public void testGetConverter_defaultConverter_returnsDefaultConverter() {
+ AppSearchDocumentConverter converter =
+ AppSearchDocumentConverterFactory.getConverter("NotRegisteredType");
+
+ assertThat(converter).isNotNull();
+ assertThat(converter).isInstanceOf(GenericDocumentConverter.class);
+ }
+}
diff --git a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/converters/GenericDocumentConverterTest.java b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/converters/GenericDocumentConverterTest.java
new file mode 100644
index 0000000..d6b73b17
--- /dev/null
+++ b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/converters/GenericDocumentConverterTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.core.google.shortcuts.converters;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.appsearch.app.GenericDocument;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.google.firebase.appindexing.Indexable;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class GenericDocumentConverterTest {
+ private final GenericDocumentConverter mConverter = new GenericDocumentConverter();
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Test
+ @SmallTest
+ public void testConvertGenericDocument_returnsIndexable() throws Exception {
+ GenericDocument childDocument1 = new GenericDocument.Builder<>(
+ "namespace1",
+ "child1",
+ "childSchema")
+ .setCreationTimestampMillis(1)
+ .build();
+ GenericDocument childDocument2 = new GenericDocument.Builder<>(
+ "namespace1",
+ "child2",
+ "childSchema")
+ .setCreationTimestampMillis(1)
+ .build();
+ GenericDocument genericDocument = new GenericDocument.Builder<>(
+ "namespace2",
+ "id1",
+ "schema")
+ .setScore(1)
+ .setTtlMillis(1000)
+ .setCreationTimestampMillis(1)
+ .setPropertyString("stringArrayProperty", "s1", "s2")
+ .setPropertyLong("longArrayProperty", 1L, 2L)
+ .setPropertyBoolean("booleanArrayProperty", true, false)
+ .setPropertyDocument("documentArrayProperty", childDocument1, childDocument2)
+ .setPropertyString("stringProperty", "s3")
+ .setPropertyLong("longProperty", 3L)
+ .setPropertyBoolean("booleanProperty", true)
+ .setPropertyDocument("documentProperty", childDocument1)
+ .setPropertyString("emptyProperty")
+ .build();
+
+ Indexable indexable = mConverter.convertGenericDocument(mContext, genericDocument).build();
+
+ Indexable expectedChild1 = new Indexable.Builder("childSchema")
+ .setMetadata(new Indexable.Metadata.Builder().setScore(0))
+ .setName("namespace1")
+ .setId("child1")
+ .setUrl("intent:#Intent;action=androidx.core.content.pm.SHORTCUT_LISTENER;"
+ + "component=androidx.core.google.shortcuts.test/androidx.core.google."
+ + "shortcuts.TrampolineActivity;S.id=child1;end")
+ .put(IndexableKeys.NAMESPACE, "namespace1")
+ .put(IndexableKeys.TTL_MILLIS, 0)
+ .put(IndexableKeys.CREATION_TIMESTAMP_MILLIS, 1)
+ .build();
+ Indexable expectedChild2 = new Indexable.Builder("childSchema")
+ .setMetadata(new Indexable.Metadata.Builder().setScore(0))
+ .setName("namespace1")
+ .setId("child2")
+ .setUrl("intent:#Intent;action=androidx.core.content.pm.SHORTCUT_LISTENER;"
+ + "component=androidx.core.google.shortcuts.test/androidx.core.google."
+ + "shortcuts.TrampolineActivity;S.id=child2;end")
+ .put(IndexableKeys.NAMESPACE, "namespace1")
+ .put(IndexableKeys.TTL_MILLIS, 0)
+ .put(IndexableKeys.CREATION_TIMESTAMP_MILLIS, 1)
+ .build();
+ Indexable expectedIndexable = new Indexable.Builder("schema")
+ .setMetadata(new Indexable.Metadata.Builder().setScore(1))
+ .setName("namespace2")
+ .setId("id1")
+ .setUrl("intent:#Intent;action=androidx.core.content.pm.SHORTCUT_LISTENER;"
+ + "component=androidx.core.google.shortcuts.test/androidx.core.google."
+ + "shortcuts.TrampolineActivity;S.id=id1;end")
+ .put(IndexableKeys.NAMESPACE, "namespace2")
+ .put(IndexableKeys.TTL_MILLIS, 1000)
+ .put(IndexableKeys.CREATION_TIMESTAMP_MILLIS, 1)
+ .put("stringArrayProperty", "s1", "s2")
+ .put("longArrayProperty", 1L, 2L)
+ .put("booleanArrayProperty", true, false)
+ .put("stringProperty", "s3")
+ .put("longProperty", 3L)
+ .put("booleanProperty", true)
+ .put("documentArrayProperty", expectedChild1, expectedChild2)
+ .put("documentProperty", expectedChild1)
+ .build();
+
+ assertThat(indexable).isEqualTo(expectedIndexable);
+ }
+}
diff --git a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/converters/TimerConverterTest.java b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/converters/TimerConverterTest.java
new file mode 100644
index 0000000..22b8a02
--- /dev/null
+++ b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/converters/TimerConverterTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.core.google.shortcuts.converters;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.provider.AlarmClock;
+
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.builtintypes.Timer;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.google.firebase.appindexing.Indexable;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.TimeZone;
+
+@RunWith(AndroidJUnit4.class)
+public class TimerConverterTest {
+ private final TimerConverter mTimerConverter = new TimerConverter();
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Test
+ @SmallTest
+ public void testConvert_returnsIndexable() throws Exception {
+ // Expire time is timezone sensitive. Force the default timezone to be GMT here.
+ TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
+
+ Timer timer = new Timer.Builder("namespace", "id")
+ .setDurationMillis(1000)
+ .setTimerStatus(Timer.STATUS_STARTED)
+ .setExpireTimeMillis(3000)
+ .setName("my timer")
+ .setRemainingTimeMillis(500)
+ .setRingtone(AlarmClock.VALUE_RINGTONE_SILENT)
+ .setScore(10)
+ .setTtlMillis(60000)
+ .setVibrate(true)
+ .build();
+
+ Indexable result = mTimerConverter.convertGenericDocument(mContext,
+ GenericDocument.fromDocumentClass(timer))
+ // Override creation timestamp to a constant instead of System.currentTimeMillis.
+ // TODO: add creation timestamp to timer.
+ .put(IndexableKeys.CREATION_TIMESTAMP_MILLIS, 1)
+ .build();
+ Indexable expectedResult = new Indexable.Builder("Timer")
+ .setMetadata(new Indexable.Metadata.Builder().setScore(10))
+ .setId("id")
+ .setName("namespace")
+ .setUrl("intent:#Intent;action=androidx.core.content.pm.SHORTCUT_LISTENER;"
+ + "component=androidx.core.google.shortcuts.test/androidx.core.google."
+ + "shortcuts.TrampolineActivity;S.id=id;end")
+ .put(IndexableKeys.NAMESPACE, "namespace")
+ .put(IndexableKeys.TTL_MILLIS, 60000)
+ .put(IndexableKeys.CREATION_TIMESTAMP_MILLIS, 1)
+ .put("length", 1000)
+ .put("timerStatus", "Started")
+ .put("expireTime", "1970-01-01T00:00:03+0000")
+ .put("message", "my timer")
+ .put("remainingTime", 500)
+ .put("ringtone", "silent")
+ .put("vibrate", true)
+ .build();
+ assertThat(result).isEqualTo(expectedResult);
+ }
+
+ @Test
+ @SmallTest
+ public void testConvert_withoutOptionalFields_returnsIndexable() throws Exception {
+ Timer timer = new Timer.Builder("namespace", "id")
+ .build();
+
+ Indexable result = mTimerConverter.convertGenericDocument(mContext,
+ GenericDocument.fromDocumentClass(timer))
+ // Override creation timestamp to a constant instead of System.currentTimeMillis.
+ // TODO: add creation timestamp to timer.
+ .put(IndexableKeys.CREATION_TIMESTAMP_MILLIS, 1)
+ .build();
+ Indexable expectedResult = new Indexable.Builder("Timer")
+ .setMetadata(new Indexable.Metadata.Builder().setScore(0))
+ .setId("id")
+ .setName("namespace")
+ .setUrl("intent:#Intent;action=androidx.core.content.pm.SHORTCUT_LISTENER;"
+ + "component=androidx.core.google.shortcuts.test/androidx.core.google."
+ + "shortcuts.TrampolineActivity;S.id=id;end")
+ .put(IndexableKeys.NAMESPACE, "namespace")
+ .put(IndexableKeys.TTL_MILLIS, 0)
+ .put(IndexableKeys.CREATION_TIMESTAMP_MILLIS, 1)
+ .put("length", 0)
+ .put("timerStatus", "Unknown")
+ .put("remainingTime", 0)
+ .put("vibrate", false)
+ .build();
+ assertThat(result).isEqualTo(expectedResult);
+ }
+}
diff --git a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/utils/ConverterUtilsTest.java b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/utils/ConverterUtilsTest.java
new file mode 100644
index 0000000..e5cc3b4
--- /dev/null
+++ b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/utils/ConverterUtilsTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.core.google.shortcuts.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.appsearch.app.GenericDocument;
+import androidx.core.google.shortcuts.converters.IndexableKeys;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.google.firebase.appindexing.Indexable;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ConverterUtilsTest {
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Test
+ @SmallTest
+ public void testBuildBaseIndexableFromGenericDocument_returnsIndexable() {
+ GenericDocument genericDocument = new GenericDocument.Builder<>(
+ "namespace", "id", "schema")
+ .setScore(1)
+ .setCreationTimestampMillis(1)
+ .build();
+
+ Indexable.Builder expectedIndexableBuilder = new Indexable.Builder("schema")
+ .setMetadata(new Indexable.Metadata.Builder().setScore(1))
+ .setId("id")
+ .setName("namespace")
+ .setUrl("intent:#Intent;action=androidx.core.content.pm.SHORTCUT_LISTENER;"
+ + "component=androidx.core.google.shortcuts.test/androidx.core.google."
+ + "shortcuts.TrampolineActivity;S.id=id;end")
+ .put(IndexableKeys.NAMESPACE, "namespace")
+ .put(IndexableKeys.TTL_MILLIS, 0)
+ .put(IndexableKeys.CREATION_TIMESTAMP_MILLIS, 1);
+ assertThat(ConverterUtils.buildBaseIndexableFromGenericDocument(
+ mContext, genericDocument).build()).isEqualTo(expectedIndexableBuilder.build());
+ }
+}
diff --git a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutUtilsTest.java b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/utils/ShortcutUtilsTest.java
similarity index 93%
rename from core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutUtilsTest.java
rename to core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/utils/ShortcutUtilsTest.java
index f30f720..3add5f7 100644
--- a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutUtilsTest.java
+++ b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/utils/ShortcutUtilsTest.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package androidx.core.google.shortcuts;
+package androidx.core.google.shortcuts.utils;
-import static androidx.core.google.shortcuts.ShortcutUtils.SHORTCUT_LISTENER_INTENT_FILTER_ACTION;
-import static androidx.core.google.shortcuts.ShortcutUtils.SHORTCUT_TAG_KEY;
-import static androidx.core.google.shortcuts.ShortcutUtils.SHORTCUT_URL_KEY;
+import static androidx.core.google.shortcuts.utils.ShortcutUtils.SHORTCUT_LISTENER_INTENT_FILTER_ACTION;
+import static androidx.core.google.shortcuts.utils.ShortcutUtils.SHORTCUT_TAG_KEY;
+import static androidx.core.google.shortcuts.utils.ShortcutUtils.SHORTCUT_URL_KEY;
import static com.google.common.truth.Truth.assertThat;
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java
index 5c5afd7..4196b1e 100644
--- a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java
@@ -18,7 +18,7 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static androidx.core.google.shortcuts.ShortcutUtils.CAPABILITY_PARAM_SEPARATOR;
+import static androidx.core.google.shortcuts.utils.ShortcutUtils.CAPABILITY_PARAM_SEPARATOR;
import android.content.Context;
import android.os.Build;
@@ -34,6 +34,7 @@
import androidx.core.google.shortcuts.builders.CapabilityBuilder;
import androidx.core.google.shortcuts.builders.ParameterBuilder;
import androidx.core.google.shortcuts.builders.ShortcutBuilder;
+import androidx.core.google.shortcuts.utils.ShortcutUtils;
import androidx.core.graphics.drawable.IconCompat;
import com.google.crypto.tink.KeysetHandle;
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/TrampolineActivity.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/TrampolineActivity.java
index adf51d0..9f5b265 100644
--- a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/TrampolineActivity.java
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/TrampolineActivity.java
@@ -17,8 +17,8 @@
package androidx.core.google.shortcuts;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-import static androidx.core.google.shortcuts.ShortcutUtils.SHORTCUT_TAG_KEY;
-import static androidx.core.google.shortcuts.ShortcutUtils.SHORTCUT_URL_KEY;
+import static androidx.core.google.shortcuts.utils.ShortcutUtils.SHORTCUT_TAG_KEY;
+import static androidx.core.google.shortcuts.utils.ShortcutUtils.SHORTCUT_URL_KEY;
import android.app.Activity;
import android.content.Intent;
@@ -28,6 +28,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.core.google.shortcuts.utils.ShortcutUtils;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.Mac;
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/AppSearchDocumentConverter.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/AppSearchDocumentConverter.java
new file mode 100644
index 0000000..54a1464
--- /dev/null
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/AppSearchDocumentConverter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.core.google.shortcuts.converters;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.GenericDocument;
+
+import com.google.firebase.appindexing.Indexable;
+
+/**
+ * Interface for a converter that will convert an AppSearch Document into {@link Indexable}.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+public interface AppSearchDocumentConverter {
+ /** Converts a @{@link GenericDocument} into a {@link Indexable.Builder}. */
+ @NonNull
+ Indexable.Builder convertGenericDocument(@NonNull Context context,
+ @NonNull GenericDocument genericDocument);
+}
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/AppSearchDocumentConverterFactory.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/AppSearchDocumentConverterFactory.java
new file mode 100644
index 0000000..c4136ad
--- /dev/null
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/AppSearchDocumentConverterFactory.java
@@ -0,0 +1,56 @@
+/*
+ * 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.core.google.shortcuts.converters;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.core.util.Preconditions;
+
+/**
+ * A factory for {@link AppSearchDocumentConverter}. Given a schema type, if the schema type has
+ * a supported converter, then return that converter. Otherwise a default
+ * {@link GenericDocumentConverter} will be returned.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+public class AppSearchDocumentConverterFactory {
+ private static final String TAG = "AppSearchDocumentConver"; // NOTYPO
+
+ /**
+ * Returns a {@link AppSearchDocumentConverter} given a schema type. If the schema type is not
+ * supported, then the {@link GenericDocumentConverter} will be returned.
+ */
+ @NonNull
+ public static AppSearchDocumentConverter getConverter(@NonNull String schemaType) {
+ Preconditions.checkNotNull(schemaType);
+
+ if ("Timer".equals(schemaType)) {
+ return new TimerConverter();
+ }
+
+ Log.w(TAG, "schema type " + schemaType + " does not have a Typed Converter registered. "
+ + "Returning the default GenericDocument converter.");
+ return new GenericDocumentConverter();
+ }
+
+ private AppSearchDocumentConverterFactory() {}
+}
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/GenericDocumentConverter.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/GenericDocumentConverter.java
new file mode 100644
index 0000000..0e15087
--- /dev/null
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/GenericDocumentConverter.java
@@ -0,0 +1,92 @@
+/*
+ * 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.core.google.shortcuts.converters;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.GenericDocument;
+import androidx.core.google.shortcuts.utils.ConverterUtils;
+import androidx.core.util.Preconditions;
+
+import com.google.firebase.appindexing.FirebaseAppIndexingInvalidArgumentException;
+import com.google.firebase.appindexing.Indexable;
+
+/**
+ * Default converter for all {@link GenericDocument}. This converter will map each property into
+ * its respective {@link Indexable} field. If a schema type is not registered with a specific
+ * converter, then this should be used as fallback.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+public class GenericDocumentConverter implements AppSearchDocumentConverter {
+ private static final String TAG = "GenericDocumentConverte"; // NOTYPO
+
+ @NonNull
+ @Override
+ public Indexable.Builder convertGenericDocument(
+ @NonNull Context context,
+ @NonNull GenericDocument genericDocument) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(genericDocument);
+
+ Indexable.Builder indexableBuilder = ConverterUtils.buildBaseIndexableFromGenericDocument(
+ context, genericDocument);
+
+ for (String property : genericDocument.getPropertyNames()) {
+ Object rawProperty = genericDocument.getProperty(property);
+ if (rawProperty instanceof String[]) {
+ indexableBuilder.put(property, (String[]) rawProperty);
+ } else if (rawProperty instanceof long[]) {
+ indexableBuilder.put(property, (long[]) rawProperty);
+ } else if (rawProperty instanceof double[]) {
+ // TODO (b/205890624): add conversion for double once it's supported in Indexable.
+ Log.w(TAG, "Property type double for " + property + " is not supported.");
+ } else if (rawProperty instanceof boolean[]) {
+ indexableBuilder.put(property, (boolean[]) rawProperty);
+ } else if (rawProperty instanceof byte[][]) {
+ // TODO (b/205890624): add conversion for byte[] once it's supported in Indexable.
+ Log.w(TAG, "Property type byte[] for " + property + " is not supported.");
+ } else if (rawProperty instanceof GenericDocument[]) {
+ try {
+ indexableBuilder.put(property,
+ convertGenericDocuments(context, (GenericDocument[]) rawProperty));
+ } catch (FirebaseAppIndexingInvalidArgumentException e) {
+ Log.e(TAG, "Cannot convert GenericDocument for property " + property);
+ }
+ } else {
+ Log.e(TAG, "Undefined property type from " + property);
+ }
+ }
+ return indexableBuilder;
+ }
+
+ private Indexable[] convertGenericDocuments(Context context,
+ GenericDocument[] genericDocuments) {
+ Indexable[] indexables = new Indexable[genericDocuments.length];
+ for (int i = 0; i < genericDocuments.length; i++) {
+ indexables[i] = convertGenericDocument(context, genericDocuments[i]).build();
+ }
+
+ return indexables;
+ }
+}
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/IndexableKeys.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/IndexableKeys.java
new file mode 100644
index 0000000..e34a460
--- /dev/null
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/IndexableKeys.java
@@ -0,0 +1,36 @@
+/*
+ * 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.core.google.shortcuts.converters;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import androidx.annotation.RestrictTo;
+
+/**
+ * Constants for the keys for common {@link com.google.firebase.appindexing.Indexable} fields.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+public class IndexableKeys {
+ // General
+ public static final String NAMESPACE = "_namespace";
+ public static final String TTL_MILLIS = "_ttlMillis";
+ public static final String CREATION_TIMESTAMP_MILLIS = "_creationTimestampMillis";
+
+ private IndexableKeys() {}
+}
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/TimerConverter.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/TimerConverter.java
new file mode 100644
index 0000000..b09f04a
--- /dev/null
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/converters/TimerConverter.java
@@ -0,0 +1,130 @@
+/*
+ * 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.core.google.shortcuts.converters;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.builtintypes.Timer;
+import androidx.core.google.shortcuts.utils.ConverterUtils;
+import androidx.core.util.Preconditions;
+
+import com.google.firebase.appindexing.Indexable;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Convert for the {@link Timer} built-in type.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+public class TimerConverter implements AppSearchDocumentConverter {
+ private static final String TAG = "TimerConverter";
+
+ // Keys from the AppSearch document
+ private static final String NAME_KEY = "name";
+ private static final String DURATION_MILLIS_KEY = "durationMillis";
+ private static final String REMAINING_TIME_MILLIS_KEY = "remainingTimeMillis";
+ private static final String RINGTONE_KEY = "ringtone";
+ private static final String VIBRATE_KEY = "vibrate";
+ private static final String TIMER_STATUS_KEY = "timerStatus";
+ private static final String EXPIRE_TIME_MILLIS_KEY = "expireTimeMillis";
+
+ // Keys for Indexables
+ private static final String MESSAGE_KEY = "message";
+ private static final String LENGTH_KEY = "length";
+ private static final String REMAINING_TIME_KEY = "remainingTime";
+ private static final String EXPIRE_TIME_KEY = "expireTime";
+
+ // Enums for TimerStatus
+ private static final String STARTED = "Started";
+ private static final String PAUSED = "Paused";
+ private static final String EXPIRED = "Expired";
+ private static final String MISSED = "Missed";
+ private static final String RESET = "Reset";
+ private static final String UNKNOWN = "Unknown";
+
+ private static final ThreadLocal<DateFormat> ISO8601_DATE_TIME_FORMAT =
+ new ThreadLocal<DateFormat>() {
+ @Override
+ public DateFormat initialValue() {
+ return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
+ }
+ };
+
+ @Override
+ @NonNull
+ public Indexable.Builder convertGenericDocument(@NonNull Context context,
+ @NonNull GenericDocument timer) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(timer);
+
+ Indexable.Builder indexableBuilder = ConverterUtils.buildBaseIndexableFromGenericDocument(
+ context, timer);
+
+ indexableBuilder
+ .put(MESSAGE_KEY, timer.getPropertyString(NAME_KEY))
+ .put(LENGTH_KEY, timer.getPropertyLong(DURATION_MILLIS_KEY))
+ .put(REMAINING_TIME_KEY, timer.getPropertyLong(REMAINING_TIME_MILLIS_KEY))
+ .put(RINGTONE_KEY, timer.getPropertyString(RINGTONE_KEY))
+ .put(VIBRATE_KEY, timer.getPropertyBoolean(VIBRATE_KEY));
+
+ int timerStatus = (int) timer.getPropertyLong(TIMER_STATUS_KEY);
+ switch (timerStatus) {
+ case Timer.STATUS_UNKNOWN:
+ indexableBuilder.put(TIMER_STATUS_KEY, UNKNOWN);
+ break;
+ case Timer.STATUS_STARTED:
+ indexableBuilder.put(TIMER_STATUS_KEY, STARTED);
+ break;
+ case Timer.STATUS_PAUSED:
+ indexableBuilder.put(TIMER_STATUS_KEY, PAUSED);
+ break;
+ case Timer.STATUS_EXPIRED:
+ indexableBuilder.put(TIMER_STATUS_KEY, EXPIRED);
+ break;
+ case Timer.STATUS_MISSED:
+ indexableBuilder.put(TIMER_STATUS_KEY, MISSED);
+ break;
+ case Timer.STATUS_RESET:
+ indexableBuilder.put(TIMER_STATUS_KEY, RESET);
+ break;
+ default:
+ indexableBuilder.put(TIMER_STATUS_KEY, UNKNOWN);
+ Log.w(TAG, "Invalud time status: " + timerStatus + ", defaulting to "
+ + "Timer.STATUS_UNKNOWN");
+ }
+
+ // 0 means never expire.
+ long expireTime = timer.getPropertyLong(EXPIRE_TIME_MILLIS_KEY);
+ if (expireTime > 0) {
+ Date date = new Date(expireTime);
+ indexableBuilder.put(EXPIRE_TIME_KEY,
+ Preconditions.checkNotNull(ISO8601_DATE_TIME_FORMAT.get()).format(date));
+ }
+ return indexableBuilder;
+ }
+}
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/utils/ConverterUtils.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/utils/ConverterUtils.java
new file mode 100644
index 0000000..35a05d9
--- /dev/null
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/utils/ConverterUtils.java
@@ -0,0 +1,60 @@
+/*
+ * 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.core.google.shortcuts.utils;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.GenericDocument;
+import androidx.core.google.shortcuts.converters.AppSearchDocumentConverter;
+import androidx.core.google.shortcuts.converters.IndexableKeys;
+import androidx.core.util.Preconditions;
+
+import com.google.firebase.appindexing.Indexable;
+
+/**
+ * Utility methods for {@link AppSearchDocumentConverter}.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY)
+public class ConverterUtils {
+ /** Creates an {@link Indexable.Builder} from {@link GenericDocument} with common fields set. */
+ @NonNull
+ public static Indexable.Builder buildBaseIndexableFromGenericDocument(
+ @NonNull Context context,
+ @NonNull GenericDocument genericDocument) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(genericDocument);
+
+ return new Indexable.Builder(genericDocument.getSchemaType())
+ .setId(genericDocument.getId())
+ // TODO (b/206020715): remove name when it's no longer a required field.
+ .setName(genericDocument.getNamespace())
+ .setMetadata(new Indexable.Metadata.Builder().setScore(genericDocument.getScore()))
+ .setUrl(ShortcutUtils.getIndexableUrl(context, genericDocument.getId()))
+ .put(IndexableKeys.NAMESPACE, genericDocument.getNamespace())
+ .put(IndexableKeys.CREATION_TIMESTAMP_MILLIS,
+ genericDocument.getCreationTimestampMillis())
+ .put(IndexableKeys.TTL_MILLIS, genericDocument.getTtlMillis());
+ }
+
+ private ConverterUtils() {}
+}
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutUtils.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/utils/ShortcutUtils.java
similarity index 94%
rename from core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutUtils.java
rename to core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/utils/ShortcutUtils.java
index 7f457d9..d6b8393 100644
--- a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutUtils.java
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/utils/ShortcutUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.core.google.shortcuts;
+package androidx.core.google.shortcuts.utils;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
@@ -26,6 +26,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.core.google.shortcuts.TrampolineActivity;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.Mac;
@@ -43,7 +44,7 @@
* @hide
*/
@RestrictTo(LIBRARY)
-class ShortcutUtils {
+public class ShortcutUtils {
public static final String ID_KEY = "id";
public static final String SHORTCUT_URL_KEY = "shortcutUrl";
public static final String CAPABILITY_PARAM_SEPARATOR = "/";
@@ -66,6 +67,7 @@
* @param shortcutId the shortcut id used to generate the url.
* @return the indexable url.
*/
+ @NonNull
public static String getIndexableUrl(@NonNull Context context, @NonNull String shortcutId) {
Intent intent = new Intent(context, TrampolineActivity.class);
intent.setAction(SHORTCUT_LISTENER_INTENT_FILTER_ACTION);
@@ -84,6 +86,7 @@
* @return the shortcut url wrapped inside an intent that opens the Trampoline Activity if
* the shortcut can be signed. Otherwise return just the shortcut url.
*/
+ @NonNull
public static String getIndexableShortcutUrl(@NonNull Context context,
@NonNull Intent shortcutIntent, @Nullable KeysetHandle keysetHandle) {
String shortcutUrl = shortcutIntent.toUri(Intent.URI_INTENT_SCHEME);
@@ -119,10 +122,12 @@
}
}
+ /** Returns whether or not the capability is a App Actions BII. */
public static boolean isAppActionCapability(@NonNull final String capability) {
return capability.startsWith(APP_ACTION_CAPABILITY_PREFIX);
}
+ /** Creates the {@link KeysetHandle} used to compute security MAC for Shortcut Urls. */
@Nullable
public static KeysetHandle getOrCreateShortcutKeysetHandle(@NonNull Context context) {
try {
diff --git a/core/core-ktx/api/current.txt b/core/core-ktx/api/current.txt
index fee3502..86d91a7 100644
--- a/core/core-ktx/api/current.txt
+++ b/core/core-ktx/api/current.txt
@@ -451,8 +451,10 @@
public final class SizeKt {
method @RequiresApi(21) public static inline operator int component1(android.util.Size);
method @RequiresApi(21) public static inline operator float component1(android.util.SizeF);
+ method public static inline operator float component1(androidx.core.util.SizeFCompat);
method @RequiresApi(21) public static inline operator int component2(android.util.Size);
method @RequiresApi(21) public static inline operator float component2(android.util.SizeF);
+ method public static inline operator float component2(androidx.core.util.SizeFCompat);
}
public final class SparseArrayKt {
diff --git a/core/core-ktx/api/public_plus_experimental_current.txt b/core/core-ktx/api/public_plus_experimental_current.txt
index fee3502..86d91a7 100644
--- a/core/core-ktx/api/public_plus_experimental_current.txt
+++ b/core/core-ktx/api/public_plus_experimental_current.txt
@@ -451,8 +451,10 @@
public final class SizeKt {
method @RequiresApi(21) public static inline operator int component1(android.util.Size);
method @RequiresApi(21) public static inline operator float component1(android.util.SizeF);
+ method public static inline operator float component1(androidx.core.util.SizeFCompat);
method @RequiresApi(21) public static inline operator int component2(android.util.Size);
method @RequiresApi(21) public static inline operator float component2(android.util.SizeF);
+ method public static inline operator float component2(androidx.core.util.SizeFCompat);
}
public final class SparseArrayKt {
diff --git a/core/core-ktx/api/restricted_current.txt b/core/core-ktx/api/restricted_current.txt
index fee3502..86d91a7 100644
--- a/core/core-ktx/api/restricted_current.txt
+++ b/core/core-ktx/api/restricted_current.txt
@@ -451,8 +451,10 @@
public final class SizeKt {
method @RequiresApi(21) public static inline operator int component1(android.util.Size);
method @RequiresApi(21) public static inline operator float component1(android.util.SizeF);
+ method public static inline operator float component1(androidx.core.util.SizeFCompat);
method @RequiresApi(21) public static inline operator int component2(android.util.Size);
method @RequiresApi(21) public static inline operator float component2(android.util.SizeF);
+ method public static inline operator float component2(androidx.core.util.SizeFCompat);
}
public final class SparseArrayKt {
diff --git a/core/core-ktx/src/androidTest/java/androidx/core/util/SizeTest.kt b/core/core-ktx/src/androidTest/java/androidx/core/util/SizeTest.kt
index 5cd063b..07aa580 100644
--- a/core/core-ktx/src/androidTest/java/androidx/core/util/SizeTest.kt
+++ b/core/core-ktx/src/androidTest/java/androidx/core/util/SizeTest.kt
@@ -23,18 +23,25 @@
import org.junit.Assert.assertEquals
import org.junit.Test
-@SdkSuppress(minSdkVersion = 21)
@SmallTest
class SizeTest {
+ @SdkSuppress(minSdkVersion = 21)
@Test fun destructuringSize() {
val (w, h) = Size(320, 240)
assertEquals(320, w)
assertEquals(240, h)
}
+ @SdkSuppress(minSdkVersion = 21)
@Test fun destructuringSizeF() {
val (w, h) = SizeF(1920.0f, 1080.0f)
assertEquals(1920.0f, w)
assertEquals(1080.0f, h)
}
+
+ @Test fun destructuringSizeFCompat() {
+ val (w, h) = SizeFCompat(1920.0f, 1080.0f)
+ assertEquals(1920.0f, w)
+ assertEquals(1080.0f, h)
+ }
}
diff --git a/core/core-ktx/src/main/java/androidx/core/util/Size.kt b/core/core-ktx/src/main/java/androidx/core/util/Size.kt
index ab2d80a..5cb85db 100644
--- a/core/core-ktx/src/main/java/androidx/core/util/Size.kt
+++ b/core/core-ktx/src/main/java/androidx/core/util/Size.kt
@@ -71,3 +71,25 @@
*/
@RequiresApi(21)
public inline operator fun SizeF.component2(): Float = height
+
+/**
+ * Returns "width", the first component of this [SizeFCompat].
+ *
+ * This method allows to use destructuring declarations when working with
+ * sizes, for example:
+ * ```
+ * val (w, h) = mySize
+ * ```
+ */
+public inline operator fun SizeFCompat.component1(): Float = width
+
+/**
+ * Returns "height", the second component of this [SizeFCompat].
+ *
+ * This method allows to use destructuring declarations when working with
+ * sizes, for example:
+ * ```
+ * val (w, h) = mySize
+ * ```
+ */
+public inline operator fun SizeFCompat.component2(): Float = height
diff --git a/core/core-performance/build.gradle b/core/core-performance/build.gradle
index 576a8d8..efefe4d 100644
--- a/core/core-performance/build.gradle
+++ b/core/core-performance/build.gradle
@@ -26,16 +26,18 @@
dependencies {
api(libs.kotlinStdlib)
+
+ testImplementation(libs.testCore)
testImplementation(libs.kotlinStdlib)
testImplementation(libs.junit)
testImplementation(libs.truth)
- testImplementation(libs.robolectric)
+ testImplementation("org.robolectric:robolectric:4.7") // TODO(b/206673076): use libs.robolectric
}
androidx {
name = "androidx.core:core-performance"
type = LibraryType.PUBLISHED_LIBRARY
- mavenVersion = LibraryVersions.CORE
+ mavenVersion = LibraryVersions.CORE_PERFORMANCE
mavenGroup = LibraryGroups.CORE
inceptionYear = "2021"
description = "This libraray makes it easy for developers to make UI and feature choices based on Android Performance Class level for GMS devices."
diff --git a/core/core-performance/samples/build.gradle b/core/core-performance/samples/build.gradle
new file mode 100644
index 0000000..1158249
--- /dev/null
+++ b/core/core-performance/samples/build.gradle
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryType
+import androidx.build.LibraryVersions
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+ api(libs.kotlinStdlib)
+ // Add dependencies here
+}
+
+androidx {
+ name = "Core Performance Samples"
+ type = LibraryType.SAMPLES
+ mavenVersion = LibraryVersions.CORE_PERFORMANCE
+ mavenGroup = LibraryGroups.CORE
+ inceptionYear = "2021"
+ description = "Samples for AndroidX Core Performance Library"
+}
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml b/core/core-performance/samples/src/androidTest/AndroidManifest.xml
similarity index 73%
copy from glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
copy to core/core-performance/samples/src/androidTest/AndroidManifest.xml
index 987c157..badc86c 100644
--- a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
+++ b/core/core-performance/samples/src/androidTest/AndroidManifest.xml
@@ -1,4 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?><!--
+<?xml version="1.0" encoding="utf-8"?>
+<!--
Copyright 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.core.performance.samples.test">
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/abc_btn_switch_to_on_mtrl_00012"
- android:insetRight="18dp"/>
\ No newline at end of file
+</manifest>
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml b/core/core-performance/samples/src/main/AndroidManifest.xml
similarity index 73%
copy from glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
copy to core/core-performance/samples/src/main/AndroidManifest.xml
index 987c157..de93147 100644
--- a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
+++ b/core/core-performance/samples/src/main/AndroidManifest.xml
@@ -1,4 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?><!--
+<?xml version="1.0" encoding="utf-8"?>
+<!--
Copyright 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.core.performance.samples">
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/abc_btn_switch_to_on_mtrl_00012"
- android:insetRight="18dp"/>
\ No newline at end of file
+</manifest>
\ No newline at end of file
diff --git a/core/core-performance/samples/src/main/androidx/core/androidx-core-core-performance-samples-documentation.md b/core/core-performance/samples/src/main/androidx/core/androidx-core-core-performance-samples-documentation.md
new file mode 100644
index 0000000..1e79883
--- /dev/null
+++ b/core/core-performance/samples/src/main/androidx/core/androidx-core-core-performance-samples-documentation.md
@@ -0,0 +1,7 @@
+# Module root
+
+<GROUPID> <ARTIFACTID>
+
+# Package androidx.core.performance.samples
+
+Insert package level documentation here
diff --git a/core/core-performance/src/main/kotlin/androidx/core/performance/PerformanceClass.kt b/core/core-performance/src/main/kotlin/androidx/core/performance/PerformanceClass.kt
index afe7ad1..538277e 100644
--- a/core/core-performance/src/main/kotlin/androidx/core/performance/PerformanceClass.kt
+++ b/core/core-performance/src/main/kotlin/androidx/core/performance/PerformanceClass.kt
@@ -16,29 +16,53 @@
package androidx.core.performance
+import android.content.Context
import android.os.Build
/**
* Reports the media performance class of the device.
*/
-class PerformanceClass {
+interface PerformanceClass {
/**
* The media performance class of the device or 0 if none.
* <p>
* If this value is not <code>0</code>, the device conforms to the media performance class
- * definition of the SDK version of this value. This value never changes while a device is
- * booted, but it may increase when the hardware manufacturer provides an OTA update.
+ * definition of the SDK version of this value. This value is stable for the duration of
+ * the process.
* <p>
* Possible non-zero values are defined in {@link Build.VERSION_CODES} starting with
* {@link Build.VERSION_CODES#R}.
* <p>
* Defaults to {@link Build.MEDIA_PERFORMANCE_CLASS}
*/
- fun getPerformanceClass(): Int {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- return Build.VERSION.MEDIA_PERFORMANCE_CLASS
+ val mediaPerformanceClass: Int
+
+ companion object {
+ /**
+ * Create PerformanceClass from the context.
+ * @param context The ApplicationContext.
+ */
+ fun create(
+ // Other implementations will require a context
+ @Suppress("UNUSED_PARAMETER") context: Context
+ ): PerformanceClass = DefaultPerformanceClassImpl()
+ }
+}
+
+/**
+ * Reports the media performance class of the device.
+ */
+private class DefaultPerformanceClassImpl : PerformanceClass {
+
+ override val mediaPerformanceClass: Int = calculateMediaPerformanceClass()
+
+ companion object {
+ fun calculateMediaPerformanceClass(): Int {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ return Build.VERSION.MEDIA_PERFORMANCE_CLASS
+ }
+ return 0
}
- return 0
}
}
diff --git a/core/core-performance/src/test/kotlin/androidx/core/performance/PerformanceClassTest.kt b/core/core-performance/src/test/kotlin/androidx/core/performance/PerformanceClassTest.kt
index 23bcaf6..01b920b 100644
--- a/core/core-performance/src/test/kotlin/androidx/core/performance/PerformanceClassTest.kt
+++ b/core/core-performance/src/test/kotlin/androidx/core/performance/PerformanceClassTest.kt
@@ -16,35 +16,49 @@
package androidx.core.performance
+import android.app.Application
import android.os.Build.VERSION_CODES.R
import android.os.Build.VERSION_CODES.S
+import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
+import org.robolectric.shadows.ShadowBuild
import org.robolectric.shadows.ShadowSystemProperties
/** Unit tests for [PerformanceClass]. */
@RunWith(RobolectricTestRunner::class)
class PerformanceClassTest {
- private val pc = PerformanceClass()
-
@Test
@Config(maxSdk = R)
- fun getPerformanceClass_sdk30() {
- assertThat(pc.getPerformanceClass()).isEqualTo(0)
+ fun getMediaPerformanceClass_sdk30() {
+ val pc = createPerformanceClass()
+ assertThat(pc.mediaPerformanceClass).isEqualTo(0)
}
@Test
- // Note this test is not actually running because robolectric does not support sdk31 yet
- @Ignore("b/206673076")
@Config(minSdk = S)
- fun getPerformanceClass_sdk31() {
+ fun getMediaPerformanceClass_sdk31_declared30() {
// TODO(b/205732671): Use ShadowBuild.setMediaPerformanceClass when available
- ShadowSystemProperties.override("ro.odm.build.media_performance_class", "31")
- assertThat(pc.getPerformanceClass()).isEqualTo(31)
+ ShadowSystemProperties.override("ro.odm.build.media_performance_class", "30")
+ ShadowBuild.reset()
+ val pc = createPerformanceClass()
+ assertThat(pc.mediaPerformanceClass).isEqualTo(30)
+ }
+
+ @Test
+ @Config(minSdk = S)
+ fun getMediaPerformanceClass_sdk31_notDeclared() {
+ // TODO(b/205732671): Use ShadowBuild.setMediaPerformanceClass when available
+ ShadowBuild.reset()
+ val pc = createPerformanceClass()
+ assertThat(pc.mediaPerformanceClass).isEqualTo(0)
+ }
+
+ private fun createPerformanceClass(): PerformanceClass {
+ return PerformanceClass.create(ApplicationProvider.getApplicationContext<Application>())
}
}
\ No newline at end of file
diff --git a/core/core-remoteviews/api/current.txt b/core/core-remoteviews/api/current.txt
index 2115270..58dee29 100644
--- a/core/core-remoteviews/api/current.txt
+++ b/core/core-remoteviews/api/current.txt
@@ -1,6 +1,13 @@
// Signature format: 4.0
package androidx.core.widget {
+ public final class AppWidgetManagerCompat {
+ method public static android.widget.RemoteViews createExactSizeAppWidget(android.appwidget.AppWidgetManager appWidgetManager, int appWidgetId, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ method public static android.widget.RemoteViews createResponsiveSizeAppWidget(android.appwidget.AppWidgetManager appWidgetManager, int appWidgetId, java.util.Collection<androidx.core.util.SizeFCompat> dpSizes, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ method public static void updateAppWidget(android.appwidget.AppWidgetManager, int appWidgetId, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ method public static void updateAppWidget(android.appwidget.AppWidgetManager, int appWidgetId, java.util.Collection<androidx.core.util.SizeFCompat> dpSizes, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ }
+
public final class RemoteViewsCompat {
method public static void setChronometerBase(android.widget.RemoteViews, @IdRes int viewId, long base);
method public static void setChronometerFormat(android.widget.RemoteViews, @IdRes int viewId, String? format);
diff --git a/core/core-remoteviews/api/public_plus_experimental_current.txt b/core/core-remoteviews/api/public_plus_experimental_current.txt
index 2115270..58dee29 100644
--- a/core/core-remoteviews/api/public_plus_experimental_current.txt
+++ b/core/core-remoteviews/api/public_plus_experimental_current.txt
@@ -1,6 +1,13 @@
// Signature format: 4.0
package androidx.core.widget {
+ public final class AppWidgetManagerCompat {
+ method public static android.widget.RemoteViews createExactSizeAppWidget(android.appwidget.AppWidgetManager appWidgetManager, int appWidgetId, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ method public static android.widget.RemoteViews createResponsiveSizeAppWidget(android.appwidget.AppWidgetManager appWidgetManager, int appWidgetId, java.util.Collection<androidx.core.util.SizeFCompat> dpSizes, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ method public static void updateAppWidget(android.appwidget.AppWidgetManager, int appWidgetId, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ method public static void updateAppWidget(android.appwidget.AppWidgetManager, int appWidgetId, java.util.Collection<androidx.core.util.SizeFCompat> dpSizes, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ }
+
public final class RemoteViewsCompat {
method public static void setChronometerBase(android.widget.RemoteViews, @IdRes int viewId, long base);
method public static void setChronometerFormat(android.widget.RemoteViews, @IdRes int viewId, String? format);
diff --git a/core/core-remoteviews/api/restricted_current.txt b/core/core-remoteviews/api/restricted_current.txt
index 8d9e929..479f021 100644
--- a/core/core-remoteviews/api/restricted_current.txt
+++ b/core/core-remoteviews/api/restricted_current.txt
@@ -1,6 +1,13 @@
// Signature format: 4.0
package androidx.core.widget {
+ public final class AppWidgetManagerCompat {
+ method public static android.widget.RemoteViews createExactSizeAppWidget(android.appwidget.AppWidgetManager appWidgetManager, int appWidgetId, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ method public static android.widget.RemoteViews createResponsiveSizeAppWidget(android.appwidget.AppWidgetManager appWidgetManager, int appWidgetId, java.util.Collection<androidx.core.util.SizeFCompat> dpSizes, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ method public static void updateAppWidget(android.appwidget.AppWidgetManager, int appWidgetId, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ method public static void updateAppWidget(android.appwidget.AppWidgetManager, int appWidgetId, java.util.Collection<androidx.core.util.SizeFCompat> dpSizes, kotlin.jvm.functions.Function1<? super androidx.core.util.SizeFCompat,? extends android.widget.RemoteViews> factory);
+ }
+
public final class RemoteViewsCompat {
method public static void setChronometerBase(android.widget.RemoteViews, @IdRes int viewId, long base);
method public static void setChronometerFormat(android.widget.RemoteViews, @IdRes int viewId, String? format);
diff --git a/core/core-remoteviews/build.gradle b/core/core-remoteviews/build.gradle
index 6b2cd78..55ff527 100644
--- a/core/core-remoteviews/build.gradle
+++ b/core/core-remoteviews/build.gradle
@@ -30,8 +30,10 @@
dependencies {
api(libs.kotlinStdlib)
api("androidx.annotation:annotation:1.2.0")
- implementation("androidx.core:core:1.7.0")
+ implementation project(":core:core")
+ androidTestImplementation project(":core:core")
+ androidTestImplementation project(":core:core-ktx")
androidTestImplementation(libs.kotlinStdlib)
androidTestImplementation(libs.kotlinTest)
androidTestImplementation(libs.testExtJunit)
@@ -40,6 +42,8 @@
androidTestImplementation(libs.testRules)
androidTestImplementation(libs.truth)
+ testImplementation project(":core:core")
+ testImplementation project(":core:core-ktx")
testImplementation(libs.testCore)
testImplementation(libs.testRunner)
testImplementation(libs.junit)
diff --git a/core/core-remoteviews/src/androidTest/java/androidx/core/widget/AppWidgetHostTestActivity.kt b/core/core-remoteviews/src/androidTest/java/androidx/core/widget/AppWidgetHostTestActivity.kt
index c5124cb..c270f6b 100644
--- a/core/core-remoteviews/src/androidTest/java/androidx/core/widget/AppWidgetHostTestActivity.kt
+++ b/core/core-remoteviews/src/androidTest/java/androidx/core/widget/AppWidgetHostTestActivity.kt
@@ -16,18 +16,27 @@
package androidx.core.widget
+import android.annotation.TargetApi
import android.app.Activity
import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetHostView
import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_SIZES
import android.content.ComponentName
+import android.os.Build
import android.os.Bundle
+import android.util.SizeF
import android.view.WindowManager
import android.widget.FrameLayout
import androidx.core.remoteviews.test.R
import org.junit.Assert.fail
/** Test activity that contains an [AppWidgetHost]. */
+@TargetApi(29)
public class AppWidgetHostTestActivity : Activity() {
private var mHost: AppWidgetHost? = null
@@ -69,6 +78,30 @@
)
)
+ fun pxToDp(px: Int): Int {
+ val density = resources.displayMetrics.density
+ return (px / density).toInt()
+ }
+
+ val width = pxToDp(contentFrame.width)
+ val height = pxToDp(contentFrame.height)
+ val optionsBundle = Bundle()
+ optionsBundle.putInt(OPTION_APPWIDGET_MIN_WIDTH, width)
+ optionsBundle.putInt(OPTION_APPWIDGET_MAX_WIDTH, width)
+ optionsBundle.putInt(OPTION_APPWIDGET_MIN_HEIGHT, height)
+ optionsBundle.putInt(OPTION_APPWIDGET_MAX_HEIGHT, height)
+ if (Build.VERSION.SDK_INT >= 31) {
+ optionsBundle.putParcelableArrayList(
+ OPTION_APPWIDGET_SIZES,
+ arrayListOf(SizeF(width.toFloat(), height.toFloat()))
+ )
+ }
+
+ appWidgetManager.updateAppWidgetOptions(
+ appWidgetId,
+ optionsBundle
+ )
+
return hostView
}
}
diff --git a/core/core-remoteviews/src/androidTest/java/androidx/core/widget/AppWidgetManagerDeviceTest.kt b/core/core-remoteviews/src/androidTest/java/androidx/core/widget/AppWidgetManagerDeviceTest.kt
new file mode 100644
index 0000000..7a1573a
--- /dev/null
+++ b/core/core-remoteviews/src/androidTest/java/androidx/core/widget/AppWidgetManagerDeviceTest.kt
@@ -0,0 +1,193 @@
+/*
+ * 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.core.widget
+
+import android.Manifest
+import android.app.UiAutomation
+import android.appwidget.AppWidgetHostView
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_SIZES
+import android.content.Context
+import android.os.Build
+import android.text.Editable
+import android.text.TextWatcher
+import android.util.SizeF
+import android.view.ViewTreeObserver
+import android.widget.RemoteViews
+import android.widget.TextView
+import androidx.core.remoteviews.test.R
+import androidx.core.util.SizeFCompat
+import androidx.core.util.component1
+import androidx.core.util.component2
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlin.test.fail
+
+@SdkSuppress(minSdkVersion = 29)
+@MediumTest
+class AppWidgetManagerDeviceTest {
+ private val mContext = ApplicationProvider.getApplicationContext<Context>()
+ private val mPackageName = mContext.packageName
+ private val mAppWidgetManager = AppWidgetManager.getInstance(mContext)
+
+ @Rule
+ @JvmField
+ public val mActivityTestRule: ActivityScenarioRule<AppWidgetHostTestActivity> =
+ ActivityScenarioRule(AppWidgetHostTestActivity::class.java)
+
+ private lateinit var mRemoteViews: RemoteViews
+ private lateinit var mHostView: AppWidgetHostView
+ private var mAppWidgetId = 0
+
+ private val mUiAutomation: UiAutomation
+ get() = InstrumentationRegistry.getInstrumentation().uiAutomation
+
+ private val mTextView: TextView
+ get() = mHostView.getChildAt(0) as TextView
+
+ @Before
+ public fun setUp() {
+ mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.BIND_APPWIDGET)
+
+ mActivityTestRule.scenario.onActivity { activity ->
+ mHostView = activity.bindAppWidget()
+ }
+
+ mAppWidgetId = mHostView.appWidgetId
+ mRemoteViews = RemoteViews(mPackageName, R.layout.remote_views_text)
+ mAppWidgetManager.updateAppWidget(mAppWidgetId, mRemoteViews)
+
+ // Wait until the remote views has been added to the host view.
+ observeDrawUntil { mHostView.childCount > 0 }
+ }
+
+ @After
+ public fun tearDown() {
+ mUiAutomation.dropShellPermissionIdentity()
+ }
+
+ @Test
+ public fun exact_shouldUseActualWidgetSize() {
+ mAppWidgetManager.updateAppWidget(mAppWidgetId) { (widthDp, heightDp) ->
+ RemoteViews(mPackageName, R.layout.remote_views_text).apply {
+ setTextViewText(R.id.text, "$widthDp x $heightDp")
+ }
+ }
+
+ val (width, height) = getSingleWidgetSize()
+ observeTextUntilEquals("$width x $height")
+ }
+
+ @Test
+ public fun responsive_shouldUseBestFittingProvidedWidgetSizes() {
+ val (width, height) = getSingleWidgetSize()
+ mAppWidgetManager.updateAppWidget(
+ mAppWidgetId,
+ listOf(width - 2 x height - 2, width + 2 x height + 2)
+ ) { (widthDp, heightDp) ->
+ RemoteViews(mPackageName, R.layout.remote_views_text).apply {
+ setTextViewText(R.id.text, "$widthDp x $heightDp")
+ }
+ }
+
+ observeTextUntilEquals("${width - 2} x ${height - 2}")
+ }
+
+ private fun observeTextUntilEquals(expectedText: String) {
+ if (mTextView.text.toString() == expectedText) return
+
+ val latch = CountDownLatch(1)
+ val textWatcher = object : TextWatcher {
+ override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
+
+ override fun onTextChanged(text: CharSequence?, p1: Int, p2: Int, p3: Int) {
+ if (text.toString() == expectedText) latch.countDown()
+ }
+
+ override fun afterTextChanged(text: Editable?) {}
+ }
+
+ mActivityTestRule.scenario.onActivity {
+ mTextView.addTextChangedListener(textWatcher)
+ }
+
+ val countedDown = latch.await(5, TimeUnit.SECONDS)
+
+ mActivityTestRule.scenario.onActivity {
+ mTextView.removeTextChangedListener(textWatcher)
+ }
+
+ if (!countedDown && mTextView.text.toString() != expectedText) {
+ fail("Expected text to be \"$expectedText\" within 5 seconds")
+ }
+ }
+
+ private fun observeDrawUntil(test: () -> Boolean) {
+ val latch = CountDownLatch(1)
+ val onDrawListener = ViewTreeObserver.OnDrawListener {
+ if (test()) latch.countDown()
+ }
+
+ mActivityTestRule.scenario.onActivity {
+ mHostView.viewTreeObserver.addOnDrawListener(onDrawListener)
+ }
+
+ val countedDown = latch.await(5, TimeUnit.SECONDS)
+
+ mActivityTestRule.scenario.onActivity {
+ mHostView.viewTreeObserver.removeOnDrawListener(onDrawListener)
+ }
+
+ if (!countedDown && !test()) {
+ fail("Expected condition to be met within 5 seconds")
+ }
+ }
+
+ private fun getSingleWidgetSize(): SizeFCompat {
+ val options = mAppWidgetManager.getAppWidgetOptions(mAppWidgetId)
+ return if (Build.VERSION.SDK_INT >= 31) {
+ options
+ .getParcelableArrayList<SizeF>(OPTION_APPWIDGET_SIZES)!!
+ .single()
+ .let { it.width x it.height }
+ } else {
+ val minWidth = options.getInt(OPTION_APPWIDGET_MIN_WIDTH)
+ val maxWidth = options.getInt(OPTION_APPWIDGET_MAX_WIDTH)
+ val minHeight = options.getInt(OPTION_APPWIDGET_MIN_HEIGHT)
+ val maxHeight = options.getInt(OPTION_APPWIDGET_MAX_HEIGHT)
+ assertThat(minWidth).isEqualTo(maxWidth)
+ assertThat(minHeight).isEqualTo(maxHeight)
+ minWidth x minHeight
+ }
+ }
+
+ private infix fun Number.x(other: Number) = SizeFCompat(this.toFloat(), other.toFloat())
+}
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml b/core/core-remoteviews/src/androidTest/res/layout/remote_views_text.xml
similarity index 77%
copy from glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
copy to core/core-remoteviews/src/androidTest/res/layout/remote_views_text.xml
index 987c157..2753a1b 100644
--- a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
+++ b/core/core-remoteviews/src/androidTest/res/layout/remote_views_text.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/abc_btn_switch_to_on_mtrl_00012"
- android:insetRight="18dp"/>
\ No newline at end of file
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
\ No newline at end of file
diff --git a/core/core-remoteviews/src/main/java/androidx/core/widget/AppWidgetManagerCompat.kt b/core/core-remoteviews/src/main/java/androidx/core/widget/AppWidgetManagerCompat.kt
new file mode 100644
index 0000000..a7135f2
--- /dev/null
+++ b/core/core-remoteviews/src/main/java/androidx/core/widget/AppWidgetManagerCompat.kt
@@ -0,0 +1,334 @@
+@file:JvmName("AppWidgetManagerCompat")
+
+/*
+ * 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.core.widget
+
+import android.appwidget.AppWidgetManager
+import android.content.res.Resources
+import android.os.Build.VERSION.SDK_INT
+import android.util.Log
+import android.util.SizeF
+import android.widget.RemoteViews
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.core.util.SizeFCompat
+import kotlin.math.ceil
+
+/** Returns whether this size is approximately at least as big as [other] in all dimensions. */
+internal infix fun SizeFCompat.approxDominates(other: SizeFCompat): Boolean {
+ return ceil(width) + 1 >= other.width && ceil(height) + 1 >= other.height
+}
+
+internal val SizeFCompat.area: Float
+ get() = width * height
+
+/**
+ * Updates the app widget with [appWidgetId], creating a [RemoteViews] for each size assigned to
+ * the app widget by [AppWidgetManager], invoking [factory] to create each alternative view.
+ *
+ * This provides ["exact" sizing](https://developer.android.com/guide/topics/appwidgets/layouts#provide-exact-layouts)
+ * , which allows you to tailor your app widget appearance to the exact size at which it is
+ * displayed. If you are only concerned with a small number of size thresholds, it is preferable
+ * to use "responsive" sizing by providing a fixed set of sizes that your app widget supports.
+ *
+ * As your [factory] may be invoked multiple times, if there is expensive computation of state that
+ * is shared among each size, it is recommended to perform that computation before calling this
+ * and cache the results as necessary.
+ *
+ * To handle resizing of your app widget, it is necessary to call this function during both
+ * [android.appwidget.AppWidgetProvider.onUpdate] and
+ * [android.appwidget.AppWidgetProvider.onAppWidgetOptionsChanged].
+ *
+ * @param appWidgetId the id of the app widget
+ * @param factory a function to create a [RemoteViews] for a given width and height (in dp)
+ */
+public fun AppWidgetManager.updateAppWidget(
+ appWidgetId: Int,
+ factory: (SizeFCompat) -> RemoteViews
+) {
+ updateAppWidget(appWidgetId, createExactSizeAppWidget(this, appWidgetId, factory))
+}
+
+/**
+ * Creates a [RemoteViews] associated with each size assigned to the app widget by
+ * [AppWidgetManager], invoking [factory] to create each alternative view.
+ *
+ * This provides ["exact" sizing](https://developer.android.com/guide/topics/appwidgets/layouts#provide-exact-layouts)
+ * , which allows you to tailor your app widget appearance to the exact size at which it is
+ * displayed. If you are only concerned with a small number of size thresholds, it is preferable
+ * to use "responsive" sizing by providing a fixed set of sizes that your app widget supports.
+ *
+ * As your [factory] may be invoked multiple times, if there is expensive computation of state that
+ * is shared among each size, it is recommended to perform that computation before calling this
+ * and cache the results as necessary.
+ *
+ * To handle resizing of your app widget, it is necessary to call [AppWidgetManager.updateAppWidget]
+ * during both [android.appwidget.AppWidgetProvider.onUpdate] and
+ * [android.appwidget.AppWidgetProvider.onAppWidgetOptionsChanged].
+ *
+ * @param appWidgetManager the [AppWidgetManager] to provide information about [appWidgetId]
+ * @param appWidgetId the id of the app widget
+ * @param factory a function to create a [RemoteViews] for a given width and height (in dp)
+ */
+public fun createExactSizeAppWidget(
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int,
+ factory: (SizeFCompat) -> RemoteViews
+): RemoteViews {
+ appWidgetManager.requireValidAppWidgetId(appWidgetId)
+ return when {
+ SDK_INT >= 31 -> {
+ AppWidgetManagerApi31Impl.createExactSizeAppWidget(
+ appWidgetManager,
+ appWidgetId,
+ factory
+ )
+ }
+ SDK_INT >= 16 -> {
+ AppWidgetManagerApi16Impl.createExactSizeAppWidget(
+ appWidgetManager,
+ appWidgetId,
+ factory
+ )
+ }
+ else -> createAppWidgetFromProviderInfo(appWidgetManager, appWidgetId, factory)
+ }
+}
+
+/**
+ * Updates the app widget with [appWidgetId], creating a [RemoteViews] for each size provided in
+ * [dpSizes].
+ *
+ * This provides ["responsive" sizing](https://developer.android.com/guide/topics/appwidgets/layouts#provide-responsive-layouts)
+ * , which allows for smoother resizing and a more consistent experience across different host
+ * configurations.
+ *
+ * As your [factory] may be invoked multiple times, if there is expensive computation of state that
+ * is shared among each size, it is recommended to perform that computation before calling this
+ * and cache the results as necessary.
+ *
+ * To handle resizing of your app widget, it is necessary to call this function during both
+ * [android.appwidget.AppWidgetProvider.onUpdate] and
+ * [android.appwidget.AppWidgetProvider.onAppWidgetOptionsChanged]. If your app's minSdk is 31 or
+ * higher, it is only necessary to call this function during `onUpdate`.
+ *
+ * @param appWidgetId the id of the app widget
+ * @param dpSizes a collection of sizes (in dp) that your app widget supports. Must not be empty or
+ * contain more than 16 elements.
+ * @param factory a function to create a [RemoteViews] for a given width and height (in dp). It is
+ * guaranteed that [factory] will only ever be called with the values provided in [dpSizes].
+ */
+public fun AppWidgetManager.updateAppWidget(
+ appWidgetId: Int,
+ dpSizes: Collection<SizeFCompat>,
+ factory: (SizeFCompat) -> RemoteViews
+) {
+ updateAppWidget(appWidgetId, createResponsiveSizeAppWidget(this, appWidgetId, dpSizes, factory))
+}
+
+/**
+ * Creating a [RemoteViews] associated with each size provided in [dpSizes].
+ *
+ * This provides ["responsive" sizing](https://developer.android.com/guide/topics/appwidgets/layouts#provide-responsive-layouts)
+ * , which allows for smoother resizing and a more consistent experience across different host
+ * configurations.
+ *
+ * As your [factory] may be invoked multiple times, if there is expensive computation of state that
+ * is shared among each size, it is recommended to perform that computation before calling this
+ * and cache the results as necessary.
+ *
+ * To handle resizing of your app widget, it is necessary to call [AppWidgetManager.updateAppWidget]
+ * during both [android.appwidget.AppWidgetProvider.onUpdate] and
+ * [android.appwidget.AppWidgetProvider.onAppWidgetOptionsChanged]. If your app's minSdk is 31 or
+ * higher, it is only necessary to call `updateAppWidget` during `onUpdate`.
+ *
+ * @param appWidgetManager the [AppWidgetManager] to provide information about [appWidgetId]
+ * @param appWidgetId the id of the app widget
+ * @param dpSizes a collection of sizes (in dp) that your app widget supports. Must not be empty or
+ * contain more than 16 elements.
+ * @param factory a function to create a [RemoteViews] for a given width and height (in dp). It is
+ * guaranteed that [factory] will only ever be called with the values provided in [dpSizes].
+ */
+public fun createResponsiveSizeAppWidget(
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int,
+ dpSizes: Collection<SizeFCompat>,
+ factory: (SizeFCompat) -> RemoteViews
+): RemoteViews {
+ appWidgetManager.requireValidAppWidgetId(appWidgetId)
+ require(dpSizes.isNotEmpty()) { "Sizes cannot be empty" }
+ require(dpSizes.size <= 16) { "At most 16 sizes may be provided" }
+ return when {
+ SDK_INT >= 31 -> AppWidgetManagerApi31Impl.createResponsiveSizeAppWidget(dpSizes, factory)
+ SDK_INT >= 16 -> {
+ AppWidgetManagerApi16Impl.createResponsiveSizeAppWidget(
+ appWidgetManager,
+ appWidgetId,
+ dpSizes,
+ factory
+ )
+ }
+ else -> createAppWidgetFromProviderInfo(appWidgetManager, appWidgetId, factory)
+ }
+}
+
+private fun AppWidgetManager.requireValidAppWidgetId(appWidgetId: Int) {
+ requireNotNull(getAppWidgetInfo(appWidgetId)) { "Invalid app widget id: $appWidgetId" }
+}
+
+@RequiresApi(31)
+private object AppWidgetManagerApi31Impl {
+ @DoNotInline
+ fun createExactSizeAppWidget(
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int,
+ factory: (SizeFCompat) -> RemoteViews
+ ): RemoteViews {
+ val options = appWidgetManager.getAppWidgetOptions(appWidgetId)
+ val sizes = options.getParcelableArrayList<SizeF>(AppWidgetManager.OPTION_APPWIDGET_SIZES)
+ if (sizes.isNullOrEmpty()) {
+ Log.w(
+ LogTag,
+ "App widget SizeF sizes not found in the options bundle, falling back to the " +
+ "min/max sizes"
+ )
+ return AppWidgetManagerApi16Impl.createExactSizeAppWidget(
+ appWidgetManager,
+ appWidgetId,
+ factory
+ )
+ }
+ return RemoteViews(sizes.associateWith { factory(it.toSizeFCompat()) })
+ }
+
+ @DoNotInline
+ fun createResponsiveSizeAppWidget(
+ dpSizes: Collection<SizeFCompat>,
+ factory: (SizeFCompat) -> RemoteViews
+ ): RemoteViews {
+ return RemoteViews(dpSizes.associate { it.toSizeF() to factory(it) })
+ }
+
+ private fun SizeF.toSizeFCompat() = SizeFCompat.createFromSizeF(this)
+}
+
+@RequiresApi(16)
+private object AppWidgetManagerApi16Impl {
+ @DoNotInline
+ fun createExactSizeAppWidget(
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int,
+ factory: (SizeFCompat) -> RemoteViews
+ ): RemoteViews {
+ val (landscapeSize, portraitSize) =
+ getSizesFromOptionsBundle(appWidgetManager, appWidgetId)
+ ?: run {
+ Log.w(
+ LogTag,
+ "App widget sizes not found in the options bundle, falling back to the " +
+ "provider size"
+ )
+ return createAppWidgetFromProviderInfo(appWidgetManager, appWidgetId, factory)
+ }
+ return createAppWidget(landscapeSize = landscapeSize, portraitSize = portraitSize, factory)
+ }
+
+ @DoNotInline
+ fun createResponsiveSizeAppWidget(
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int,
+ sizes: Collection<SizeFCompat>,
+ factory: (SizeFCompat) -> RemoteViews
+ ): RemoteViews {
+ val minSize = sizes.minByOrNull { it.area } ?: error("Sizes cannot be empty")
+ val (landscapeSize, portraitSize) =
+ getSizesFromOptionsBundle(appWidgetManager, appWidgetId)
+ ?: run {
+ Log.w(
+ LogTag,
+ "App widget sizes not found in the options bundle, falling back to the " +
+ "smallest supported size ($minSize)"
+ )
+ LandscapePortraitSizes(minSize, minSize)
+ }
+ val effectiveLandscapeSize =
+ sizes.filter { landscapeSize approxDominates it }.maxByOrNull { it.area } ?: minSize
+ val effectivePortraitSize =
+ sizes.filter { portraitSize approxDominates it }.maxByOrNull { it.area } ?: minSize
+ return createAppWidget(
+ landscapeSize = effectiveLandscapeSize,
+ portraitSize = effectivePortraitSize,
+ factory
+ )
+ }
+
+ private fun createAppWidget(
+ landscapeSize: SizeFCompat,
+ portraitSize: SizeFCompat,
+ factory: (SizeFCompat) -> RemoteViews
+ ): RemoteViews {
+ return if (landscapeSize == portraitSize) {
+ factory(landscapeSize)
+ } else {
+ RemoteViews(
+ /* landscape= */ factory(landscapeSize),
+ /* portrait= */ factory(portraitSize)
+ )
+ }
+ }
+
+ private fun getSizesFromOptionsBundle(
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int
+ ): LandscapePortraitSizes? {
+ val options = appWidgetManager.getAppWidgetOptions(appWidgetId)
+
+ val portWidthDp = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, -1)
+ val portHeightDp = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, -1)
+ if (portWidthDp < 0 || portHeightDp < 0) return null
+
+ val landWidthDp = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, -1)
+ val landHeightDp = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, -1)
+ if (landWidthDp < 0 || landHeightDp < 0) return null
+
+ return LandscapePortraitSizes(
+ landscape = SizeFCompat(landWidthDp.toFloat(), landHeightDp.toFloat()),
+ portrait = SizeFCompat(portWidthDp.toFloat(), portHeightDp.toFloat())
+ )
+ }
+}
+
+internal fun createAppWidgetFromProviderInfo(
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int,
+ factory: (SizeFCompat) -> RemoteViews
+): RemoteViews {
+ return factory(appWidgetManager.getSizeFromProviderInfo(appWidgetId))
+}
+
+internal fun AppWidgetManager.getSizeFromProviderInfo(appWidgetId: Int): SizeFCompat {
+ val providerInfo = getAppWidgetInfo(appWidgetId)
+ fun pxToDp(value: Int) = (value / Resources.getSystem().displayMetrics.density)
+ val width = pxToDp(providerInfo.minWidth)
+ val height = pxToDp(providerInfo.minHeight)
+ return SizeFCompat(width, height)
+}
+
+internal data class LandscapePortraitSizes(val landscape: SizeFCompat, val portrait: SizeFCompat)
+
+private const val LogTag = "AppWidgetManagerCompat"
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
new file mode 100644
index 0000000..9b160b0
--- /dev/null
+++ b/core/core-remoteviews/src/test/java/androidx/core/widget/AppWidgetManagerCompatTest.kt
@@ -0,0 +1,283 @@
+/*
+ * 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.core.widget
+
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT
+import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.os.Bundle
+import android.widget.FrameLayout
+import android.widget.RemoteViews
+import android.widget.TextView
+import androidx.annotation.RequiresApi
+import androidx.core.remoteviews.test.R
+import androidx.core.util.SizeFCompat
+import androidx.core.util.component1
+import androidx.core.util.component2
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.Shadows.shadowOf
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+import org.robolectric.util.ReflectionHelpers
+import kotlin.test.assertFailsWith
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(sdk = [17])
+@RequiresApi(17)
+class AppWidgetManagerCompatTest {
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private val packageName = context.packageName
+ private val appWidgetManager = AppWidgetManager.getInstance(context)
+ private val appWidgetId = 1
+ private val landscapeContext = configurationContext { orientation = ORIENTATION_LANDSCAPE }
+ private val portraitContext = configurationContext { orientation = ORIENTATION_PORTRAIT }
+
+ @Test
+ fun exact_oneSize_shouldUpdateRemoteViewsWithoutLandPort() {
+ setWidgetSize(100 x 200)
+
+ val rv = createExactSizeAppWidget(appWidgetManager, appWidgetId) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+
+ assertThat(rv.landscape).isNull()
+ assertThat(rv.portrait).isNull()
+ }
+
+ @Test
+ fun exact_multipleSizes_shouldUpdateRemoteViewsWithLandPort() {
+ setWidgetSize(landscape = 200 x 100, portrait = 110 x 190)
+
+ val rv = createExactSizeAppWidget(appWidgetManager, appWidgetId) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+
+ assertThat(rv.landscape).isNotNull()
+ assertThat(rv.portrait).isNotNull()
+ }
+
+ @Test
+ fun exact_multipleSizes_shouldUseLandscapeSizeForLandscapeView() {
+ setWidgetSize(landscape = 200 x 100, portrait = 110 x 190)
+
+ val rv = createExactSizeAppWidget(appWidgetManager, appWidgetId) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+
+ val view = rv.apply(landscapeContext, FrameLayout(landscapeContext))
+ assertThat(view.findViewById<TextView>(R.id.text).text.toString())
+ .isEqualTo("200.0 x 100.0")
+ }
+
+ @Test
+ fun exact_multipleSizes_shouldUsePortraitSizeForPortraitView() {
+ setWidgetSize(landscape = 200 x 100, portrait = 110 x 190)
+
+ val rv = createExactSizeAppWidget(appWidgetManager, appWidgetId) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+
+ val view = rv.apply(portraitContext, FrameLayout(portraitContext))
+ assertThat(view.findViewById<TextView>(R.id.text).text.toString())
+ .isEqualTo("110.0 x 190.0")
+ }
+
+ @Test
+ fun exact_invalidWidget_shouldThrow() {
+ assertFailsWith<IllegalArgumentException> {
+ createExactSizeAppWidget(appWidgetManager, appWidgetId) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun responsive_singleSizeFromLauncher_shouldChooseBestFitAsSingleRemoteViews() {
+ setWidgetSize(100 x 200)
+
+ val rv = createResponsiveSizeAppWidget(
+ appWidgetManager,
+ appWidgetId,
+ listOf(50 x 50, 75 x 75, 120 x 140)
+ ) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+
+ assertThat(rv.landscape).isNull()
+ assertThat(rv.portrait).isNull()
+ val view = rv.apply(context, FrameLayout(context))
+ assertThat(view.findViewById<TextView>(R.id.text).text.toString())
+ .isEqualTo("75.0 x 75.0")
+ }
+
+ @Test
+ fun responsive_multipleSizesFromLauncher_bestFitIsSameForBoth_shouldChooseBestFitAsSingle() {
+ setWidgetSize(landscape = 200 x 100, portrait = 110 x 190)
+
+ val rv = createResponsiveSizeAppWidget(
+ appWidgetManager,
+ appWidgetId,
+ listOf(50 x 50, 75 x 75, 120 x 140)
+ ) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+
+ assertThat(rv.landscape).isNull()
+ assertThat(rv.portrait).isNull()
+ val view = rv.apply(context, FrameLayout(context))
+ assertThat(view.findViewById<TextView>(R.id.text).text.toString()).isEqualTo("75.0 x 75.0")
+ }
+
+ @Test
+ fun responsive_multipleSizesFromLauncher_bestFitIsDifferent_shouldChooseBestFitsAsLandPort() {
+ setWidgetSize(landscape = 200 x 100, portrait = 110 x 190)
+
+ val rv = createResponsiveSizeAppWidget(
+ appWidgetManager,
+ appWidgetId,
+ listOf(50 x 50, 75 x 75, 120 x 100)
+ ) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+
+ assertThat(rv.landscape).isNotNull()
+ assertThat(rv.portrait).isNotNull()
+ val landView = rv.apply(landscapeContext, FrameLayout(landscapeContext))
+ assertThat(landView.findViewById<TextView>(R.id.text).text.toString())
+ .isEqualTo("120.0 x 100.0")
+ val portView = rv.apply(portraitContext, FrameLayout(portraitContext))
+ assertThat(portView.findViewById<TextView>(R.id.text).text.toString())
+ .isEqualTo("75.0 x 75.0")
+ }
+
+ @Test
+ fun responsive_noSizes_shouldThrow() {
+ setWidgetSize(landscape = 200 x 100, portrait = 110 x 190)
+
+ assertFailsWith<IllegalArgumentException> {
+ createResponsiveSizeAppWidget(
+ appWidgetManager,
+ appWidgetId,
+ emptyList()
+ ) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun responsive_tooManySizes_shouldThrow() {
+ setWidgetSize(landscape = 200 x 100, portrait = 110 x 190)
+
+ assertFailsWith<IllegalArgumentException> {
+ createResponsiveSizeAppWidget(
+ appWidgetManager,
+ appWidgetId,
+ (1..17).map { it x it }
+ ) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun responsive_invalidWidget_shouldThrow() {
+ assertFailsWith<IllegalArgumentException> {
+ createResponsiveSizeAppWidget(
+ appWidgetManager,
+ appWidgetId,
+ listOf(100 x 100)
+ ) { (width, height) ->
+ remoteViews {
+ setTextViewText(R.id.text, "$width x $height")
+ }
+ }
+ }
+ }
+
+ private fun setWidgetSize(landscape: SizeFCompat, portrait: SizeFCompat? = null) {
+ shadowOf(appWidgetManager).addBoundWidget(appWidgetId, AppWidgetProviderInfo())
+ appWidgetManager.updateAppWidgetOptions(
+ appWidgetId,
+ Bundle().apply {
+ putInt(OPTION_APPWIDGET_MAX_WIDTH, landscape.width.toInt())
+ putInt(OPTION_APPWIDGET_MIN_HEIGHT, landscape.height.toInt())
+
+ putInt(
+ OPTION_APPWIDGET_MIN_WIDTH,
+ portrait?.width?.toInt() ?: landscape.width.toInt()
+ )
+ putInt(
+ OPTION_APPWIDGET_MAX_HEIGHT,
+ portrait?.height?.toInt() ?: landscape.height.toInt()
+ )
+ }
+ )
+ }
+
+ private infix fun Int.x(other: Int) = SizeFCompat(this.toFloat(), other.toFloat())
+
+ private fun remoteViews(modifier: RemoteViews.() -> Unit = {}): RemoteViews {
+ return RemoteViews(packageName, R.layout.remote_views).apply(modifier)
+ }
+
+ private val RemoteViews.landscape: RemoteViews?
+ get() = ReflectionHelpers.getField(this, "mLandscape")
+
+ private val RemoteViews.portrait: RemoteViews?
+ get() = ReflectionHelpers.getField(this, "mPortrait")
+
+ @RequiresApi(17)
+ private fun configurationContext(modifier: Configuration.() -> Unit): Context {
+ val configuration = Configuration()
+ configuration.apply(modifier)
+ return context.createConfigurationContext(configuration)
+ }
+}
\ No newline at end of file
diff --git a/core/core-splashscreen/src/main/java/androidx/core/splashscreen/SplashScreen.kt b/core/core-splashscreen/src/main/java/androidx/core/splashscreen/SplashScreen.kt
index 57fef19..46694db 100644
--- a/core/core-splashscreen/src/main/java/androidx/core/splashscreen/SplashScreen.kt
+++ b/core/core-splashscreen/src/main/java/androidx/core/splashscreen/SplashScreen.kt
@@ -65,7 +65,7 @@
* This needs to be called before [Activity.setContentView] or other view operation on
* the root view (e.g setting flags).
*
- * Alternatively, if a [SplashScreen] instance is not required, the them can manually be
+ * Alternatively, if a [SplashScreen] instance is not required, the theme can manually be
* set using [Activity.setTheme].
*/
@JvmStatic
@@ -343,4 +343,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index e2580ac..399b948 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1106,9 +1106,11 @@
method public boolean isDeclaredInManifest();
method public boolean isDynamic();
method public boolean isEnabled();
+ method public boolean isExcludedFrom(int);
method public boolean isImmutable();
method public boolean isPinned();
method @RequiresApi(25) public android.content.pm.ShortcutInfo! toShortcutInfo();
+ field public static final int SURFACE_LAUNCHER = 1; // 0x1
}
public static class ShortcutInfoCompat.Builder {
@@ -1120,12 +1122,12 @@
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setAlwaysBadged();
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setCategories(java.util.Set<java.lang.String!>);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(CharSequence);
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExcludedSurfaces(int);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExtras(android.os.PersistableBundle);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIcon(androidx.core.graphics.drawable.IconCompat!);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsConversation();
- method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsHiddenFromLauncher(boolean);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
@@ -2030,6 +2032,14 @@
method public boolean test(T!);
}
+ public final class SizeFCompat {
+ ctor public SizeFCompat(float, float);
+ method @RequiresApi(21) public static androidx.core.util.SizeFCompat createFromSizeF(android.util.SizeF);
+ method public float getHeight();
+ method public float getWidth();
+ method @RequiresApi(21) public android.util.SizeF toSizeF();
+ }
+
public interface Supplier<T> {
method public T! get();
}
@@ -2040,29 +2050,29 @@
public class AccessibilityDelegateCompat {
ctor public AccessibilityDelegateCompat();
- method public boolean dispatchPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat! getAccessibilityNodeProvider(android.view.View!);
- method public void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public void onInitializeAccessibilityNodeInfo(android.view.View!, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
- method public void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public boolean performAccessibilityAction(android.view.View!, int, android.os.Bundle!);
- method public void sendAccessibilityEvent(android.view.View!, int);
- method public void sendAccessibilityEventUnchecked(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+ method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat? getAccessibilityNodeProvider(android.view.View);
+ method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat);
+ method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+ method public void sendAccessibilityEvent(android.view.View, int);
+ method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
}
public abstract class ActionProvider {
- ctor public ActionProvider(android.content.Context!);
- method public android.content.Context! getContext();
+ ctor public ActionProvider(android.content.Context);
+ method public android.content.Context getContext();
method public boolean hasSubMenu();
method public boolean isVisible();
- method public abstract android.view.View! onCreateActionView();
- method public android.view.View! onCreateActionView(android.view.MenuItem!);
+ method public abstract android.view.View onCreateActionView();
+ method public android.view.View onCreateActionView(android.view.MenuItem);
method public boolean onPerformDefaultAction();
- method public void onPrepareSubMenu(android.view.SubMenu!);
+ method public void onPrepareSubMenu(android.view.SubMenu);
method public boolean overridesItemVisibility();
method public void refreshVisibility();
- method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener!);
+ method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener?);
}
public static interface ActionProvider.VisibilityListener {
@@ -2112,7 +2122,7 @@
}
public final class DisplayCutoutCompat {
- ctor public DisplayCutoutCompat(android.graphics.Rect!, java.util.List<android.graphics.Rect!>!);
+ ctor public DisplayCutoutCompat(android.graphics.Rect?, java.util.List<android.graphics.Rect!>?);
ctor public DisplayCutoutCompat(androidx.core.graphics.Insets, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, androidx.core.graphics.Insets);
method public java.util.List<android.graphics.Rect!> getBoundingRects();
method public int getSafeInsetBottom();
@@ -2127,16 +2137,16 @@
}
public class DragStartHelper {
- ctor public DragStartHelper(android.view.View!, androidx.core.view.DragStartHelper.OnDragStartListener!);
+ ctor public DragStartHelper(android.view.View, androidx.core.view.DragStartHelper.OnDragStartListener);
method public void attach();
method public void detach();
- method public void getTouchPosition(android.graphics.Point!);
- method public boolean onLongClick(android.view.View!);
- method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+ method public void getTouchPosition(android.graphics.Point);
+ method public boolean onLongClick(android.view.View);
+ method public boolean onTouch(android.view.View, android.view.MotionEvent);
}
public static interface DragStartHelper.OnDragStartListener {
- method public boolean onDragStart(android.view.View!, androidx.core.view.DragStartHelper!);
+ method public boolean onDragStart(android.view.View, androidx.core.view.DragStartHelper);
}
public final class GestureDetectorCompat {
@@ -3431,9 +3441,9 @@
package androidx.core.view.animation {
public final class PathInterpolatorCompat {
- method public static android.view.animation.Interpolator! create(android.graphics.Path!);
- method public static android.view.animation.Interpolator! create(float, float);
- method public static android.view.animation.Interpolator! create(float, float, float, float);
+ method public static android.view.animation.Interpolator create(android.graphics.Path);
+ method public static android.view.animation.Interpolator create(float, float);
+ method public static android.view.animation.Interpolator create(float, float, float, float);
}
}
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index 98fa24a..03bbcc0 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -1106,9 +1106,11 @@
method public boolean isDeclaredInManifest();
method public boolean isDynamic();
method public boolean isEnabled();
+ method public boolean isExcludedFrom(int);
method public boolean isImmutable();
method public boolean isPinned();
method @RequiresApi(25) public android.content.pm.ShortcutInfo! toShortcutInfo();
+ field public static final int SURFACE_LAUNCHER = 1; // 0x1
}
public static class ShortcutInfoCompat.Builder {
@@ -1120,12 +1122,12 @@
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setAlwaysBadged();
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setCategories(java.util.Set<java.lang.String!>);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(CharSequence);
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExcludedSurfaces(int);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExtras(android.os.PersistableBundle);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIcon(androidx.core.graphics.drawable.IconCompat!);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsConversation();
- method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsHiddenFromLauncher(boolean);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
@@ -2034,6 +2036,14 @@
method public boolean test(T!);
}
+ public final class SizeFCompat {
+ ctor public SizeFCompat(float, float);
+ method @RequiresApi(21) public static androidx.core.util.SizeFCompat createFromSizeF(android.util.SizeF);
+ method public float getHeight();
+ method public float getWidth();
+ method @RequiresApi(21) public android.util.SizeF toSizeF();
+ }
+
public interface Supplier<T> {
method public T! get();
}
@@ -2044,29 +2054,29 @@
public class AccessibilityDelegateCompat {
ctor public AccessibilityDelegateCompat();
- method public boolean dispatchPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat! getAccessibilityNodeProvider(android.view.View!);
- method public void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public void onInitializeAccessibilityNodeInfo(android.view.View!, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
- method public void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public boolean performAccessibilityAction(android.view.View!, int, android.os.Bundle!);
- method public void sendAccessibilityEvent(android.view.View!, int);
- method public void sendAccessibilityEventUnchecked(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+ method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat? getAccessibilityNodeProvider(android.view.View);
+ method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat);
+ method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+ method public void sendAccessibilityEvent(android.view.View, int);
+ method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
}
public abstract class ActionProvider {
- ctor public ActionProvider(android.content.Context!);
- method public android.content.Context! getContext();
+ ctor public ActionProvider(android.content.Context);
+ method public android.content.Context getContext();
method public boolean hasSubMenu();
method public boolean isVisible();
- method public abstract android.view.View! onCreateActionView();
- method public android.view.View! onCreateActionView(android.view.MenuItem!);
+ method public abstract android.view.View onCreateActionView();
+ method public android.view.View onCreateActionView(android.view.MenuItem);
method public boolean onPerformDefaultAction();
- method public void onPrepareSubMenu(android.view.SubMenu!);
+ method public void onPrepareSubMenu(android.view.SubMenu);
method public boolean overridesItemVisibility();
method public void refreshVisibility();
- method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener!);
+ method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener?);
}
public static interface ActionProvider.VisibilityListener {
@@ -2116,7 +2126,7 @@
}
public final class DisplayCutoutCompat {
- ctor public DisplayCutoutCompat(android.graphics.Rect!, java.util.List<android.graphics.Rect!>!);
+ ctor public DisplayCutoutCompat(android.graphics.Rect?, java.util.List<android.graphics.Rect!>?);
ctor public DisplayCutoutCompat(androidx.core.graphics.Insets, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, androidx.core.graphics.Insets);
method public java.util.List<android.graphics.Rect!> getBoundingRects();
method public int getSafeInsetBottom();
@@ -2131,16 +2141,16 @@
}
public class DragStartHelper {
- ctor public DragStartHelper(android.view.View!, androidx.core.view.DragStartHelper.OnDragStartListener!);
+ ctor public DragStartHelper(android.view.View, androidx.core.view.DragStartHelper.OnDragStartListener);
method public void attach();
method public void detach();
- method public void getTouchPosition(android.graphics.Point!);
- method public boolean onLongClick(android.view.View!);
- method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+ method public void getTouchPosition(android.graphics.Point);
+ method public boolean onLongClick(android.view.View);
+ method public boolean onTouch(android.view.View, android.view.MotionEvent);
}
public static interface DragStartHelper.OnDragStartListener {
- method public boolean onDragStart(android.view.View!, androidx.core.view.DragStartHelper!);
+ method public boolean onDragStart(android.view.View, androidx.core.view.DragStartHelper);
}
public final class GestureDetectorCompat {
@@ -3435,9 +3445,9 @@
package androidx.core.view.animation {
public final class PathInterpolatorCompat {
- method public static android.view.animation.Interpolator! create(android.graphics.Path!);
- method public static android.view.animation.Interpolator! create(float, float);
- method public static android.view.animation.Interpolator! create(float, float, float, float);
+ method public static android.view.animation.Interpolator create(android.graphics.Path);
+ method public static android.view.animation.Interpolator create(float, float);
+ method public static android.view.animation.Interpolator create(float, float, float, float);
}
}
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 65f68dd..85539f6 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -1226,9 +1226,11 @@
method public boolean isDeclaredInManifest();
method public boolean isDynamic();
method public boolean isEnabled();
+ method public boolean isExcludedFrom(@androidx.core.content.pm.ShortcutInfoCompat.Surface int);
method public boolean isImmutable();
method public boolean isPinned();
method @RequiresApi(25) public android.content.pm.ShortcutInfo! toShortcutInfo();
+ field public static final int SURFACE_LAUNCHER = 1; // 0x1
}
public static class ShortcutInfoCompat.Builder {
@@ -1242,12 +1244,12 @@
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setAlwaysBadged();
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setCategories(java.util.Set<java.lang.String!>);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setDisabledMessage(CharSequence);
+ method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExcludedSurfaces(int);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setExtras(android.os.PersistableBundle);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIcon(androidx.core.graphics.drawable.IconCompat!);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntent(android.content.Intent);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIntents(android.content.Intent![]);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsConversation();
- method public androidx.core.content.pm.ShortcutInfoCompat.Builder setIsHiddenFromLauncher(boolean);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLocusId(androidx.core.content.LocusIdCompat?);
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLabel(CharSequence);
method @Deprecated public androidx.core.content.pm.ShortcutInfoCompat.Builder setLongLived();
@@ -1259,6 +1261,9 @@
method public androidx.core.content.pm.ShortcutInfoCompat.Builder setSliceUri(android.net.Uri);
}
+ @IntDef({androidx.core.content.pm.ShortcutInfoCompat.SURFACE_LAUNCHER}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ShortcutInfoCompat.Surface {
+ }
+
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class ShortcutInfoCompatSaver<T> {
ctor public ShortcutInfoCompatSaver();
method @AnyThread public abstract T! addShortcuts(java.util.List<androidx.core.content.pm.ShortcutInfoCompat!>!);
@@ -2400,6 +2405,7 @@
method public static void checkArgument(boolean);
method public static void checkArgument(boolean, Object);
method public static void checkArgument(boolean, String, java.lang.Object!...);
+ method public static float checkArgumentFinite(float, String);
method public static int checkArgumentInRange(int, int, int, String);
method public static long checkArgumentInRange(long, long, long, String);
method public static float checkArgumentInRange(float, float, float, String);
@@ -2420,6 +2426,14 @@
method public boolean test(T!);
}
+ public final class SizeFCompat {
+ ctor public SizeFCompat(float, float);
+ method @RequiresApi(21) public static androidx.core.util.SizeFCompat createFromSizeF(android.util.SizeF);
+ method public float getHeight();
+ method public float getWidth();
+ method @RequiresApi(21) public android.util.SizeF toSizeF();
+ }
+
public interface Supplier<T> {
method public T! get();
}
@@ -2438,32 +2452,32 @@
public class AccessibilityDelegateCompat {
ctor public AccessibilityDelegateCompat();
- ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public AccessibilityDelegateCompat(android.view.View.AccessibilityDelegate!);
- method public boolean dispatchPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat! getAccessibilityNodeProvider(android.view.View!);
- method public void onInitializeAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public void onInitializeAccessibilityNodeInfo(android.view.View!, androidx.core.view.accessibility.AccessibilityNodeInfoCompat!);
- method public void onPopulateAccessibilityEvent(android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup!, android.view.View!, android.view.accessibility.AccessibilityEvent!);
- method public boolean performAccessibilityAction(android.view.View!, int, android.os.Bundle!);
- method public void sendAccessibilityEvent(android.view.View!, int);
- method public void sendAccessibilityEventUnchecked(android.view.View!, android.view.accessibility.AccessibilityEvent!);
+ ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public AccessibilityDelegateCompat(android.view.View.AccessibilityDelegate);
+ method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public androidx.core.view.accessibility.AccessibilityNodeProviderCompat? getAccessibilityNodeProvider(android.view.View);
+ method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public void onInitializeAccessibilityNodeInfo(android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat);
+ method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
+ method public void sendAccessibilityEvent(android.view.View, int);
+ method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
}
public abstract class ActionProvider {
- ctor public ActionProvider(android.content.Context!);
- method public android.content.Context! getContext();
+ ctor public ActionProvider(android.content.Context);
+ method public android.content.Context getContext();
method public boolean hasSubMenu();
method public boolean isVisible();
- method public abstract android.view.View! onCreateActionView();
- method public android.view.View! onCreateActionView(android.view.MenuItem!);
+ method public abstract android.view.View onCreateActionView();
+ method public android.view.View onCreateActionView(android.view.MenuItem);
method public boolean onPerformDefaultAction();
- method public void onPrepareSubMenu(android.view.SubMenu!);
+ method public void onPrepareSubMenu(android.view.SubMenu);
method public boolean overridesItemVisibility();
method public void refreshVisibility();
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void reset();
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSubUiVisibilityListener(androidx.core.view.ActionProvider.SubUiVisibilityListener!);
- method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setSubUiVisibilityListener(androidx.core.view.ActionProvider.SubUiVisibilityListener?);
+ method public void setVisibilityListener(androidx.core.view.ActionProvider.VisibilityListener?);
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void subUiVisibilityChanged(boolean);
}
@@ -2524,7 +2538,7 @@
}
public final class DisplayCutoutCompat {
- ctor public DisplayCutoutCompat(android.graphics.Rect!, java.util.List<android.graphics.Rect!>!);
+ ctor public DisplayCutoutCompat(android.graphics.Rect?, java.util.List<android.graphics.Rect!>?);
ctor public DisplayCutoutCompat(androidx.core.graphics.Insets, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, android.graphics.Rect?, androidx.core.graphics.Insets);
method public java.util.List<android.graphics.Rect!> getBoundingRects();
method public int getSafeInsetBottom();
@@ -2536,20 +2550,20 @@
public final class DragAndDropPermissionsCompat {
method public void release();
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.view.DragAndDropPermissionsCompat? request(android.app.Activity!, android.view.DragEvent!);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static androidx.core.view.DragAndDropPermissionsCompat? request(android.app.Activity, android.view.DragEvent);
}
public class DragStartHelper {
- ctor public DragStartHelper(android.view.View!, androidx.core.view.DragStartHelper.OnDragStartListener!);
+ ctor public DragStartHelper(android.view.View, androidx.core.view.DragStartHelper.OnDragStartListener);
method public void attach();
method public void detach();
- method public void getTouchPosition(android.graphics.Point!);
- method public boolean onLongClick(android.view.View!);
- method public boolean onTouch(android.view.View!, android.view.MotionEvent!);
+ method public void getTouchPosition(android.graphics.Point);
+ method public boolean onLongClick(android.view.View);
+ method public boolean onTouch(android.view.View, android.view.MotionEvent);
}
public static interface DragStartHelper.OnDragStartListener {
- method public boolean onDragStart(android.view.View!, androidx.core.view.DragStartHelper!);
+ method public boolean onDragStart(android.view.View, androidx.core.view.DragStartHelper);
}
public final class GestureDetectorCompat {
@@ -3891,9 +3905,9 @@
package androidx.core.view.animation {
public final class PathInterpolatorCompat {
- method public static android.view.animation.Interpolator! create(android.graphics.Path!);
- method public static android.view.animation.Interpolator! create(float, float);
- method public static android.view.animation.Interpolator! create(float, float, float, float);
+ method public static android.view.animation.Interpolator create(android.graphics.Path);
+ method public static android.view.animation.Interpolator create(float, float);
+ method public static android.view.animation.Interpolator create(float, float, float, float);
}
}
diff --git a/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutManagerCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutManagerCompatTest.java
index 4bb9442..ecd3d65 100644
--- a/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutManagerCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/pm/ShortcutManagerCompatTest.java
@@ -290,38 +290,39 @@
@LargeTest
@Test
@SdkSuppress(minSdkVersion = 25)
- public void testHiddenShortcuts() throws Throwable {
+ public void testExcludedShortcuts() throws Throwable {
final ShortcutManager mockShortcutManager = mock(ShortcutManager.class);
doReturn(mockShortcutManager).when(mContext).getSystemService(eq(Context.SHORTCUT_SERVICE));
+ when(mockShortcutManager.getMaxShortcutCountPerActivity()).thenReturn(5);
- final ShortcutInfoCompat hiddenShortcut1 = new ShortcutInfoCompat.Builder(
+ final ShortcutInfoCompat excludedShortcut1 = new ShortcutInfoCompat.Builder(
mContext, "my-shortcut")
.setShortLabel("bitmap")
.setIcon(createBitmapIcon())
.setIntent(new Intent().setAction(Intent.ACTION_DEFAULT))
- .setIsHiddenFromLauncher(true)
+ .setExcludedSurfaces(ShortcutInfoCompat.SURFACE_LAUNCHER)
.build();
- final ShortcutInfoCompat hiddenShortcut2 = new ShortcutInfoCompat.Builder(
+ final ShortcutInfoCompat excludedShortcut2 = new ShortcutInfoCompat.Builder(
mContext, "my-shortcut-2")
.setShortLabel("bitmap")
.setIcon(createBitmapIcon())
.setIntent(new Intent().setAction(Intent.ACTION_DEFAULT))
- .setIsHiddenFromLauncher(true)
+ .setExcludedSurfaces(ShortcutInfoCompat.SURFACE_LAUNCHER)
.build();
- final ShortcutInfoCompat hiddenShortcut3 = new ShortcutInfoCompat.Builder(
+ final ShortcutInfoCompat excludedShortcut3 = new ShortcutInfoCompat.Builder(
mContext, "my-shortcut-3")
.setShortLabel("bitmap")
.setIcon(createBitmapIcon())
.setIntent(new Intent().setAction(Intent.ACTION_DEFAULT))
- .setIsHiddenFromLauncher(true)
+ .setExcludedSurfaces(ShortcutInfoCompat.SURFACE_LAUNCHER)
.build();
- final List<ShortcutInfoCompat> hiddenShortcuts = new ArrayList<>();
- hiddenShortcuts.add(hiddenShortcut1);
- hiddenShortcuts.add(hiddenShortcut2);
- hiddenShortcuts.add(hiddenShortcut3);
+ final List<ShortcutInfoCompat> excludedShortcuts = new ArrayList<>();
+ excludedShortcuts.add(excludedShortcut1);
+ excludedShortcuts.add(excludedShortcut2);
+ excludedShortcuts.add(excludedShortcut3);
- ShortcutManagerCompat.addDynamicShortcuts(mContext, hiddenShortcuts);
+ ShortcutManagerCompat.addDynamicShortcuts(mContext, excludedShortcuts);
if (Build.VERSION.SDK_INT > 31) {
verify(mockShortcutManager).addDynamicShortcuts(
ArgumentMatchers.<ShortcutInfo>anyList());
@@ -332,21 +333,25 @@
reset(mockShortcutManager);
when(mockShortcutManager.setDynamicShortcuts(ArgumentMatchers.<ShortcutInfo>anyList()))
.thenReturn(true);
+ when(mockShortcutManager.getMaxShortcutCountPerActivity()).thenReturn(5);
- ShortcutManagerCompat.setDynamicShortcuts(mContext, hiddenShortcuts);
+ ShortcutManagerCompat.setDynamicShortcuts(mContext, excludedShortcuts);
if (Build.VERSION.SDK_INT > 31) {
verify(mockShortcutManager).setDynamicShortcuts(
ArgumentMatchers.<ShortcutInfo>anyList());
- } else if (Build.VERSION.SDK_INT >= 25) {
- verify(mockShortcutManager).setDynamicShortcuts(Collections.EMPTY_LIST);
+ } else {
+ if (Build.VERSION.SDK_INT >= 25) {
+ verify(mockShortcutManager).setDynamicShortcuts(Collections.EMPTY_LIST);
+ }
+ verify(mShortcutInfoCompatSaver).removeAllShortcuts();
+ verify(mShortcutInfoCompatSaver).addShortcuts(Collections.EMPTY_LIST);
}
- verify(mShortcutInfoCompatSaver).removeAllShortcuts();
verify(mShortcutInfoChangeListener).onAllShortcutsRemoved();
- verify(mShortcutInfoCompatSaver).addShortcuts(Collections.EMPTY_LIST);
- verify(mShortcutInfoChangeListener).onShortcutAdded(hiddenShortcuts);
+ verify(mShortcutInfoChangeListener).onShortcutAdded(excludedShortcuts);
reset(mockShortcutManager);
- ShortcutManagerCompat.pushDynamicShortcut(mContext, hiddenShortcut1);
+ when(mockShortcutManager.getMaxShortcutCountPerActivity()).thenReturn(5);
+ ShortcutManagerCompat.pushDynamicShortcut(mContext, excludedShortcut1);
if (Build.VERSION.SDK_INT > 31) {
verify(mockShortcutManager).pushDynamicShortcut(any(ShortcutInfo.class));
} else if (Build.VERSION.SDK_INT >= 30) {
@@ -357,7 +362,8 @@
}
reset(mockShortcutManager);
- ShortcutManagerCompat.updateShortcuts(mContext, hiddenShortcuts);
+ when(mockShortcutManager.getMaxShortcutCountPerActivity()).thenReturn(5);
+ ShortcutManagerCompat.updateShortcuts(mContext, excludedShortcuts);
if (Build.VERSION.SDK_INT > 31) {
verify(mockShortcutManager).updateShortcuts(anyList());
} else if (Build.VERSION.SDK_INT >= 25) {
@@ -365,7 +371,8 @@
}
reset(mockShortcutManager);
- ShortcutManagerCompat.enableShortcuts(mContext, hiddenShortcuts);
+ when(mockShortcutManager.getMaxShortcutCountPerActivity()).thenReturn(5);
+ ShortcutManagerCompat.enableShortcuts(mContext, excludedShortcuts);
if (Build.VERSION.SDK_INT > 31) {
verify(mockShortcutManager).enableShortcuts(anyList());
} else if (Build.VERSION.SDK_INT >= 25) {
diff --git a/core/core/src/androidTest/java/androidx/core/view/AccessibilityDelegateCompatTest.java b/core/core/src/androidTest/java/androidx/core/view/AccessibilityDelegateCompatTest.java
index 3c5ea44..a0c489b 100644
--- a/core/core/src/androidTest/java/androidx/core/view/AccessibilityDelegateCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/view/AccessibilityDelegateCompatTest.java
@@ -47,6 +47,8 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityEventCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
@@ -167,27 +169,29 @@
@Test
@SdkSuppress(minSdkVersion = 19, maxSdkVersion = 27)
- @FlakyTest(bugId = 187190911)
- public void testAccessibilityPaneTitle_isntTrackedAsPaneWithoutTitle() {
- // This test isn't to test the propagation up, just that the event is sent correctly
- ViewCompat.setAccessibilityLiveRegion(mView,
- ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
-
- ViewCompat.setAccessibilityPaneTitle(mView, "Sample title");
-
- ViewCompat.setAccessibilityPaneTitle(mView, null);
-
+ public void testAccessibilityPaneTitle_isntTrackedAsPaneWithoutTitle() throws Throwable {
+ // This test isn't to test the propagation up, just that the event-sending behavior
final AccessibilityDelegateCompat mockDelegate = mock(
AccessibilityDelegateCompat.class);
- ViewCompat.setAccessibilityDelegate(mView, new BridgingDelegateCompat(mockDelegate));
+ mActivityTestRule.runOnUiThread(() -> {
+ ViewCompat.setAccessibilityLiveRegion(mView,
+ ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE);
- mView.setVisibility(View.VISIBLE);
+ ViewCompat.setAccessibilityPaneTitle(mView, "Sample title");
- mView.getViewTreeObserver().dispatchOnGlobalLayout();
- ArgumentCaptor<AccessibilityEvent> argumentCaptor =
- ArgumentCaptor.forClass(AccessibilityEvent.class);
- verify(mockDelegate, never()).sendAccessibilityEventUnchecked(
- eq(mView), argumentCaptor.capture());
+ ViewCompat.setAccessibilityPaneTitle(mView, null);
+
+ ViewCompat.setAccessibilityDelegate(mView, new BridgingDelegateCompat(mockDelegate));
+
+ mView.setVisibility(View.VISIBLE);
+
+ mView.getViewTreeObserver().dispatchOnGlobalLayout();
+
+ ArgumentCaptor<AccessibilityEvent> argumentCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+ verify(mockDelegate, never()).sendAccessibilityEventUnchecked(
+ eq(mView), argumentCaptor.capture());
+ });
}
@Test
@@ -627,48 +631,54 @@
}
@Override
- public void sendAccessibilityEvent(View host, int eventType) {
+ public void sendAccessibilityEvent(@NonNull View host, int eventType) {
mMockCompat.sendAccessibilityEvent(host, eventType);
}
@Override
- public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
+ public void sendAccessibilityEventUnchecked(@NonNull View host,
+ @NonNull AccessibilityEvent event) {
mMockCompat.sendAccessibilityEventUnchecked(host, event);
}
@Override
- public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ public boolean dispatchPopulateAccessibilityEvent(
+ @NonNull View host, @NonNull AccessibilityEvent event) {
return mMockCompat.dispatchPopulateAccessibilityEvent(host, event);
}
@Override
- public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ public void onPopulateAccessibilityEvent(
+ @NonNull View host, @NonNull AccessibilityEvent event) {
mMockCompat.onPopulateAccessibilityEvent(host, event);
}
@Override
- public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ public void onInitializeAccessibilityEvent(
+ @NonNull View host, @NonNull AccessibilityEvent event) {
mMockCompat.onInitializeAccessibilityEvent(host, event);
}
@Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+ public void onInitializeAccessibilityNodeInfo(
+ @NonNull View host, @NonNull AccessibilityNodeInfoCompat info) {
mMockCompat.onInitializeAccessibilityNodeInfo(host, info);
}
@Override
- public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
- AccessibilityEvent event) {
+ public boolean onRequestSendAccessibilityEvent(@NonNull ViewGroup host, @NonNull View child,
+ @NonNull AccessibilityEvent event) {
return mMockCompat.onRequestSendAccessibilityEvent(host, child, event);
}
@Override
- public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
+ public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(@NonNull View host) {
return mMockCompat.getAccessibilityNodeProvider(host);
}
@Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ public boolean performAccessibilityAction(
+ @NonNull View host, int action, @Nullable Bundle args) {
return mMockCompat.performAccessibilityAction(host, action, args);
}
}
diff --git a/core/core/src/androidTest/java/androidx/core/view/DragStartHelperTest.java b/core/core/src/androidTest/java/androidx/core/view/DragStartHelperTest.java
index abd2d15..b32fa94 100644
--- a/core/core/src/androidTest/java/androidx/core/view/DragStartHelperTest.java
+++ b/core/core/src/androidTest/java/androidx/core/view/DragStartHelperTest.java
@@ -76,7 +76,7 @@
private DragStartHelper createDragStartHelper(final DragStartListener listener) {
return new DragStartHelper(mDragSource, new DragStartHelper.OnDragStartListener() {
@Override
- public boolean onDragStart(View v, DragStartHelper helper) {
+ public boolean onDragStart(@NonNull View v, @NonNull DragStartHelper helper) {
Point touchPosition = new Point();
helper.getTouchPosition(touchPosition);
return listener.onDragStart(v, helper, touchPosition);
diff --git a/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java b/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
index 67c796886..e6fd010 100644
--- a/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/content/pm/ShortcutInfoCompat.java
@@ -31,6 +31,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@@ -41,6 +42,8 @@
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.net.UriCompat;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -61,6 +64,17 @@
private static final String EXTRA_SLICE_URI = "extraSliceUri";
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @IntDef({SURFACE_LAUNCHER})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Surface {}
+
+ /**
+ * Indicates system surfaces managed by a launcher app. e.g. Long-Press Menu.
+ */
+ public static final int SURFACE_LAUNCHER = 1 << 0;
+
Context mContext;
String mId;
String mPackageName;
@@ -97,7 +111,7 @@
boolean mIsEnabled = true;
boolean mHasKeyFieldsOnly;
int mDisabledReason;
- boolean mIsHiddenFromLauncher;
+ int mExcludedSurfaces;
ShortcutInfoCompat() { }
@@ -480,6 +494,13 @@
}
/**
+ * Return true if the shortcut is excluded from specified surface.
+ */
+ public boolean isExcludedFrom(@Surface final int surface) {
+ return (mExcludedSurfaces & surface) > 0;
+ }
+
+ /**
* @hide
*/
@RequiresApi(25)
@@ -546,7 +567,7 @@
if (shortcutInfo.mExtras != null) {
mInfo.mExtras = shortcutInfo.mExtras;
}
- mInfo.mIsHiddenFromLauncher = shortcutInfo.mIsHiddenFromLauncher;
+ mInfo.mExcludedSurfaces = shortcutInfo.mExcludedSurfaces;
}
/**
@@ -788,20 +809,23 @@
}
/**
- * Sets if a shortcut is considered hidden from Launcher. If so, shortcuts will be
- * excluded from the search result of {@link android.content.pm.LauncherApps#getShortcuts(
- * android.content.pm.LauncherApps.ShortcutQuery, UserHandle)}. This generally means the
- * shortcut would not be displayed by a launcher app (e.g. in Long-Press menu), while remain
- * visible in other surfaces such as assistant or on-device-intelligence.
+ * Sets which surfaces a shortcut will be excluded from.
*
- * <p>On API <= 31, shortcuts that are hidden from launcher are not actually sent to
- * {@link ShortcutManager}. These shortcuts might still be made available to other surfaces
- * via alternative means.
+ * If the shortcut is set to be excluded from {@link #SURFACE_LAUNCHER}, shortcuts will be
+ * excluded from the search result of {@link android.content.pm.LauncherApps#getShortcuts(
+ * android.content.pm.LauncherApps.ShortcutQuery, UserHandle)} and
+ * {@link android.content.pm.ShortcutManager#getShortcuts(int)}. This generally means the
+ * shortcut would not be displayed by a launcher app (e.g. in Long-Press menu), while
+ * remain visible in other surfaces such as assistant or on-device-intelligence.
+ *
+ * <p>On API <= 31, shortcuts that are excluded from {@link #SURFACE_LAUNCHER} are not
+ * actually sent to {@link ShortcutManager}. These shortcuts might still be made
+ * available to other surfaces via alternative means.
*/
@SuppressWarnings("MissingGetterMatchingBuilder")
@NonNull
- public Builder setIsHiddenFromLauncher(boolean isHidden) {
- mInfo.mIsHiddenFromLauncher = isHidden;
+ public Builder setExcludedSurfaces(final int surfaces) {
+ mInfo.mExcludedSurfaces = surfaces;
return this;
}
diff --git a/core/core/src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java b/core/core/src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java
index adcad12..f730e64 100644
--- a/core/core/src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java
+++ b/core/core/src/main/java/androidx/core/content/pm/ShortcutManagerCompat.java
@@ -188,7 +188,8 @@
*/
public static boolean requestPinShortcut(@NonNull final Context context,
@NonNull ShortcutInfoCompat shortcut, @Nullable final IntentSender callback) {
- if (Build.VERSION.SDK_INT <= 31 && shortcut.mIsHiddenFromLauncher) {
+ if (Build.VERSION.SDK_INT <= 31
+ && shortcut.isExcludedFrom(ShortcutInfoCompat.SURFACE_LAUNCHER)) {
// A shortcut that is not frequently used cannot be pinned to WorkSpace.
return false;
}
@@ -307,8 +308,8 @@
/**
* Publish the list of dynamic shortcuts. If there are already dynamic or pinned shortcuts with
* the same IDs, each mutable shortcut is updated.
- * <p>On API <= 31 Any shortcuts that are marked as hidden from launcher will not be passed to
- * the {@link ShortcutManager}, but they might still be available to assistant and other
+ * <p>On API <= 31 Any shortcuts that are marked as excluded from launcher will not be passed
+ * to the {@link ShortcutManager}, but they might still be available to assistant and other
* surfaces through alternative means.
*
* <p>This API will be rate-limited.
@@ -321,7 +322,8 @@
*/
public static boolean addDynamicShortcuts(@NonNull Context context,
@NonNull List<ShortcutInfoCompat> shortcutInfoList) {
- final List<ShortcutInfoCompat> clone = excludesHiddenShortcuts(shortcutInfoList);
+ final List<ShortcutInfoCompat> clone = removeShortcutsExcludedFromSurface(
+ shortcutInfoList, ShortcutInfoCompat.SURFACE_LAUNCHER);
if (Build.VERSION.SDK_INT <= 29) {
convertUriIconsToBitmapIcons(context, clone);
}
@@ -436,8 +438,8 @@
* Publish the list of shortcuts. All existing dynamic shortcuts from the caller app
* will be replaced. If there are already pinned shortcuts with the same IDs,
* the mutable pinned shortcuts are updated.
- * <p>On API <= 31 Any shortcuts that are marked as hidden from launcher will not be passed to
- * the {@link ShortcutManager}, but they might still be available to assistant and other
+ * <p>On API <= 31 Any shortcuts that are marked as excluded from launcher will not be passed
+ * to the {@link ShortcutManager}, but they might still be available to assistant and other
* surfaces through alternative means.
*
* <p>This API will be rate-limited.
@@ -460,7 +462,8 @@
@NonNull final List<ShortcutInfoCompat> shortcutInfoList) {
Preconditions.checkNotNull(context);
Preconditions.checkNotNull(shortcutInfoList);
- final List<ShortcutInfoCompat> clone = excludesHiddenShortcuts(shortcutInfoList);
+ final List<ShortcutInfoCompat> clone = removeShortcutsExcludedFromSurface(
+ shortcutInfoList, ShortcutInfoCompat.SURFACE_LAUNCHER);
if (Build.VERSION.SDK_INT >= 25) {
List<ShortcutInfo> shortcuts = new ArrayList<>(clone.size());
for (ShortcutInfoCompat compat : clone) {
@@ -511,8 +514,8 @@
/**
* Update all existing shortcuts with the same IDs. Target shortcuts may be pinned and/or
* dynamic, but they must not be immutable.
- * <p>On API <= 31 Any shortcuts that are marked as hidden from launcher will not be passed to
- * the {@link ShortcutManager}, but they might still be available to assistant and other
+ * <p>On API <= 31 Any shortcuts that are marked as excluded from launcher will not be passed
+ * to the {@link ShortcutManager}, but they might still be available to assistant and other
* surfaces through alternative means.
*
* <p>This API will be rate-limited.
@@ -524,7 +527,8 @@
*/
public static boolean updateShortcuts(@NonNull Context context,
@NonNull List<ShortcutInfoCompat> shortcutInfoList) {
- final List<ShortcutInfoCompat> clone = excludesHiddenShortcuts(shortcutInfoList);
+ final List<ShortcutInfoCompat> clone = removeShortcutsExcludedFromSurface(
+ shortcutInfoList, ShortcutInfoCompat.SURFACE_LAUNCHER);
if (Build.VERSION.SDK_INT <= 29) {
convertUriIconsToBitmapIcons(context, clone);
}
@@ -613,7 +617,8 @@
/**
* Re-enable pinned shortcuts that were previously disabled. If the target shortcuts
* are already enabled, this method does nothing.
- * <p>In API 31 and below any shortcuts that are marked as hidden from launcher will be ignored.
+ * <p>In API 31 and below any shortcuts that are marked as excluded from launcher will be
+ * ignored.
*
* Compatibility behavior:
* <ul>
@@ -627,7 +632,8 @@
*/
public static void enableShortcuts(@NonNull final Context context,
@NonNull final List<ShortcutInfoCompat> shortcutInfoList) {
- final List<ShortcutInfoCompat> clone = excludesHiddenShortcuts(shortcutInfoList);
+ final List<ShortcutInfoCompat> clone = removeShortcutsExcludedFromSurface(
+ shortcutInfoList, ShortcutInfoCompat.SURFACE_LAUNCHER);
if (Build.VERSION.SDK_INT >= 25) {
final ArrayList<String> shortcutIds = new ArrayList<>(shortcutInfoList.size());
for (ShortcutInfoCompat shortcut : clone) {
@@ -742,7 +748,8 @@
Preconditions.checkNotNull(context);
Preconditions.checkNotNull(shortcut);
- if (Build.VERSION.SDK_INT <= 31 && shortcut.mIsHiddenFromLauncher) {
+ if (Build.VERSION.SDK_INT <= 31
+ && shortcut.isExcludedFrom(ShortcutInfoCompat.SURFACE_LAUNCHER)) {
for (ShortcutInfoChangeListener listener : getShortcutInfoListeners(context)) {
listener.onShortcutAdded(Collections.singletonList(shortcut));
}
@@ -897,13 +904,13 @@
}
@NonNull
- private static List<ShortcutInfoCompat> excludesHiddenShortcuts(
- @NonNull final List<ShortcutInfoCompat> shortcuts) {
+ private static List<ShortcutInfoCompat> removeShortcutsExcludedFromSurface(
+ @NonNull final List<ShortcutInfoCompat> shortcuts, final int surfaces) {
Objects.requireNonNull(shortcuts);
if (Build.VERSION.SDK_INT > 31) return shortcuts;
final List<ShortcutInfoCompat> clone = new ArrayList<>(shortcuts);
for (ShortcutInfoCompat si: shortcuts) {
- if (si.mIsHiddenFromLauncher) {
+ if (si.isExcludedFrom(surfaces)) {
clone.remove(si);
}
}
diff --git a/core/core/src/main/java/androidx/core/util/Preconditions.java b/core/core/src/main/java/androidx/core/util/Preconditions.java
index ce979e0..3461d12 100644
--- a/core/core/src/main/java/androidx/core/util/Preconditions.java
+++ b/core/core/src/main/java/androidx/core/util/Preconditions.java
@@ -336,6 +336,29 @@
return value;
}
+ /**
+ * Ensures that the argument floating point value is a finite number.
+ *
+ * <p>A finite number is defined to be both representable (that is, not NaN) and
+ * not infinite (that is neither positive or negative infinity).</p>
+ *
+ * @param value a floating point value
+ * @param valueName the name of the argument to use if the check fails
+ *
+ * @return the validated floating point value
+ *
+ * @throws IllegalArgumentException if {@code value} was not finite
+ */
+ public static float checkArgumentFinite(float value, @NonNull String valueName) {
+ if (Float.isNaN(value)) {
+ throw new IllegalArgumentException(valueName + " must not be NaN");
+ } else if (Float.isInfinite(value)) {
+ throw new IllegalArgumentException(valueName + " must not be infinite");
+ }
+
+ return value;
+ }
+
private Preconditions() {
}
}
diff --git a/core/core/src/main/java/androidx/core/util/SizeFCompat.java b/core/core/src/main/java/androidx/core/util/SizeFCompat.java
new file mode 100644
index 0000000..124a1ec
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/util/SizeFCompat.java
@@ -0,0 +1,106 @@
+/*
+ * 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.core.util;
+
+import android.util.SizeF;
+
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+/**
+ * Immutable class for describing width and height dimensions in some arbitrary unit. Width and
+ * height are finite values stored as a floating point representation.
+ * <p>
+ * This is a backward-compatible version of {@link SizeF}.
+ */
+public final class SizeFCompat {
+
+ private final float mWidth;
+ private final float mHeight;
+
+ public SizeFCompat(float width, float height) {
+ mWidth = Preconditions.checkArgumentFinite(width, "width");
+ mHeight = Preconditions.checkArgumentFinite(height, "height");
+ }
+
+ /**
+ * Get the width of the size (as an arbitrary unit).
+ * @return width
+ */
+ public float getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Get the height of the size (as an arbitrary unit).
+ * @return height
+ */
+ public float getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SizeFCompat)) return false;
+ SizeFCompat that = (SizeFCompat) o;
+ return that.mWidth == mWidth && that.mHeight == mHeight;
+ }
+
+ @Override
+ public int hashCode() {
+ return Float.floatToIntBits(mWidth) ^ Float.floatToIntBits(mHeight);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return mWidth + "x" + mHeight;
+ }
+
+ /** Converts this {@link SizeFCompat} into a {@link SizeF}. */
+ @RequiresApi(21)
+ @NonNull
+ public SizeF toSizeF() {
+ return Api21Impl.toSizeF(this);
+ }
+
+ /** Creates a {@link SizeFCompat} from a {@link SizeF}. */
+ @RequiresApi(21)
+ @NonNull
+ public static SizeFCompat createFromSizeF(@NonNull SizeF size) {
+ return Api21Impl.toSizeFCompat(size);
+ }
+
+ @RequiresApi(21)
+ private static final class Api21Impl {
+ @DoNotInline
+ @NonNull
+ static SizeFCompat toSizeFCompat(@NonNull SizeF size) {
+ Preconditions.checkNotNull(size);
+ return new SizeFCompat(size.getWidth(), size.getHeight());
+ }
+
+ @DoNotInline
+ @NonNull
+ static SizeF toSizeF(@NonNull SizeFCompat size) {
+ Preconditions.checkNotNull(size);
+ return new SizeF(size.getWidth(), size.getHeight());
+ }
+ }
+}
diff --git a/core/core/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java b/core/core/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java
index de873ba..75698dd 100644
--- a/core/core/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java
+++ b/core/core/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java
@@ -18,6 +18,7 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import android.accessibilityservice.AccessibilityService;
import android.os.Build;
import android.os.Bundle;
import android.text.style.ClickableSpan;
@@ -29,6 +30,9 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.R;
@@ -45,14 +49,14 @@
* Helper for accessing {@link AccessibilityDelegate}.
* <p>
* <strong>Note:</strong> On platform versions prior to
- * {@link android.os.Build.VERSION_CODES#M API 23}, delegate methods on
+ * {@link Build.VERSION_CODES#M API 23}, delegate methods on
* views in the {@code android.widget.*} package are called <i>before</i>
* host methods. This prevents certain properties such as class name from
* being modified by overriding
* {@link AccessibilityDelegateCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)},
* as any changes will be overwritten by the host class.
* <p>
- * Starting in {@link android.os.Build.VERSION_CODES#M API 23}, delegate
+ * Starting in {@link Build.VERSION_CODES#M API 23}, delegate
* methods are called <i>after</i> host methods, which all properties to be
* modified without being overwritten by the host class.
*/
@@ -144,7 +148,7 @@
* @hide
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
- public AccessibilityDelegateCompat(AccessibilityDelegate originalDelegate) {
+ public AccessibilityDelegateCompat(@NonNull AccessibilityDelegate originalDelegate) {
mOriginalDelegate = originalDelegate;
mBridge = new AccessibilityDelegateAdapter(this);
}
@@ -170,7 +174,7 @@
*
* @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int)
*/
- public void sendAccessibilityEvent(View host, int eventType) {
+ public void sendAccessibilityEvent(@NonNull View host, int eventType) {
mOriginalDelegate.sendAccessibilityEvent(host, eventType);
}
@@ -192,7 +196,8 @@
* @see View#sendAccessibilityEventUnchecked(AccessibilityEvent)
* View#sendAccessibilityEventUnchecked(AccessibilityEvent)
*/
- public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
+ public void sendAccessibilityEventUnchecked(@NonNull View host,
+ @NonNull AccessibilityEvent event) {
mOriginalDelegate.sendAccessibilityEventUnchecked(host, event);
}
@@ -213,7 +218,8 @@
* @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
* View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
*/
- public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ public boolean dispatchPopulateAccessibilityEvent(@NonNull View host,
+ @NonNull AccessibilityEvent event) {
return mOriginalDelegate.dispatchPopulateAccessibilityEvent(host, event);
}
@@ -233,7 +239,8 @@
* @see ViewCompat#onPopulateAccessibilityEvent(View ,AccessibilityEvent)
* ViewCompat#onPopulateAccessibilityEvent(View, AccessibilityEvent)
*/
- public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ public void onPopulateAccessibilityEvent(@NonNull View host,
+ @NonNull AccessibilityEvent event) {
mOriginalDelegate.onPopulateAccessibilityEvent(host, event);
}
@@ -253,7 +260,8 @@
* @see ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
* ViewCompat#onInitializeAccessibilityEvent(View, AccessibilityEvent)
*/
- public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ public void onInitializeAccessibilityEvent(@NonNull View host,
+ @NonNull AccessibilityEvent event) {
mOriginalDelegate.onInitializeAccessibilityEvent(host, event);
}
@@ -272,7 +280,8 @@
* @see ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
* ViewCompat#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfoCompat)
*/
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+ @NonNull AccessibilityNodeInfoCompat info) {
mOriginalDelegate.onInitializeAccessibilityNodeInfo(
host, info.unwrap());
}
@@ -296,14 +305,14 @@
* @see ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
* ViewGroupCompat#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)
*/
- public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
- AccessibilityEvent event) {
+ public boolean onRequestSendAccessibilityEvent(@NonNull ViewGroup host, @NonNull View child,
+ @NonNull AccessibilityEvent event) {
return mOriginalDelegate.onRequestSendAccessibilityEvent(host, child, event);
}
/**
* Gets the provider for managing a virtual view hierarchy rooted at this View
- * and reported to {@link android.accessibilityservice.AccessibilityService}s
+ * and reported to {@link AccessibilityService}s
* that explore the window content.
* <p>
* The default implementation behaves as
@@ -315,9 +324,10 @@
*
* @see AccessibilityNodeProviderCompat
*/
- public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
+ @Nullable
+ public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(@NonNull View host) {
if (Build.VERSION.SDK_INT >= 16) {
- Object provider = mOriginalDelegate.getAccessibilityNodeProvider(host);
+ Object provider = Api16Impl.getAccessibilityNodeProvider(mOriginalDelegate, host);
if (provider != null) {
return new AccessibilityNodeProviderCompat(provider);
}
@@ -335,13 +345,17 @@
* no accessibility delegate been set.
* </p>
*
+ *
+ * @param host View on which to perform the action.
* @param action The action to perform.
+ * @param args Optional action arguments.
* @return Whether the action was performed.
*
* @see View#performAccessibilityAction(int, Bundle)
* View#performAccessibilityAction(int, Bundle)
*/
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ public boolean performAccessibilityAction(@NonNull View host, int action,
+ @NonNull Bundle args) {
boolean success = false;
List<AccessibilityActionCompat> actions = getActionList(host);
for (int i = 0; i < actions.size(); i++) {
@@ -352,7 +366,7 @@
}
}
if (!success && Build.VERSION.SDK_INT >= 16) {
- success = mOriginalDelegate.performAccessibilityAction(host, action, args);
+ success = Api16Impl.performAccessibilityAction(mOriginalDelegate, host, action, args);
}
if (!success && action == R.id.accessibility_action_clickable_span) {
success = performClickableSpanAction(
@@ -396,6 +410,25 @@
static List<AccessibilityActionCompat> getActionList(View view) {
List<AccessibilityActionCompat> actions = (List<AccessibilityActionCompat>)
view.getTag(R.id.tag_accessibility_actions);
- return actions == null ? Collections.<AccessibilityActionCompat>emptyList() : actions;
+ return actions == null ? Collections.emptyList() : actions;
+ }
+
+ @RequiresApi(16)
+ static class Api16Impl {
+ private Api16Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static AccessibilityNodeProvider getAccessibilityNodeProvider(
+ AccessibilityDelegate accessibilityDelegate, View host) {
+ return accessibilityDelegate.getAccessibilityNodeProvider(host);
+ }
+
+ @DoNotInline
+ static boolean performAccessibilityAction(AccessibilityDelegate accessibilityDelegate,
+ View host, int action, Bundle args) {
+ return accessibilityDelegate.performAccessibilityAction(host, action, args);
+ }
}
}
diff --git a/core/core/src/main/java/androidx/core/view/ActionProvider.java b/core/core/src/main/java/androidx/core/view/ActionProvider.java
index 468e67f..dff98c6 100644
--- a/core/core/src/main/java/androidx/core/view/ActionProvider.java
+++ b/core/core/src/main/java/androidx/core/view/ActionProvider.java
@@ -24,6 +24,8 @@
import android.view.SubMenu;
import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
/**
@@ -130,13 +132,14 @@
*
* @param context Context for accessing resources.
*/
- public ActionProvider(Context context) {
+ public ActionProvider(@NonNull Context context) {
mContext = context;
}
/**
* Gets the context associated with this action provider.
*/
+ @NonNull
public Context getContext() {
return mContext;
}
@@ -146,6 +149,7 @@
*
* @return A new action view.
*/
+ @NonNull
public abstract View onCreateActionView();
/**
@@ -160,7 +164,9 @@
* @param forItem MenuItem to create the action view for
* @return the new action view
*/
- public View onCreateActionView(MenuItem forItem) {
+ @SuppressWarnings("unused")
+ @NonNull
+ public View onCreateActionView(@NonNull MenuItem forItem) {
return onCreateActionView();
}
@@ -259,7 +265,8 @@
*
* @param subMenu Submenu that will be displayed
*/
- public void onPrepareSubMenu(SubMenu subMenu) {
+ @SuppressWarnings("unused")
+ public void onPrepareSubMenu(@NonNull SubMenu subMenu) {
}
/**
@@ -279,7 +286,7 @@
* @hide Internal use only
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
- public void setSubUiVisibilityListener(SubUiVisibilityListener listener) {
+ public void setSubUiVisibilityListener(@Nullable SubUiVisibilityListener listener) {
mSubUiVisibilityListener = listener;
}
@@ -289,7 +296,7 @@
*
* @param listener listener to set
*/
- public void setVisibilityListener(VisibilityListener listener) {
+ public void setVisibilityListener(@Nullable VisibilityListener listener) {
if (mVisibilityListener != null && listener != null) {
Log.w(TAG, "setVisibilityListener: Setting a new ActionProvider.VisibilityListener " +
"when one is already set. Are you reusing this " + getClass().getSimpleName() +
diff --git a/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java b/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java
index e29026b..5b02d20 100644
--- a/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java
+++ b/core/core/src/main/java/androidx/core/view/ContentInfoCompat.java
@@ -36,6 +36,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.function.Predicate;
/**
@@ -127,6 +128,7 @@
/**
* Flag requesting that the content should be converted to plain text prior to inserting.
*/
+ @SuppressWarnings("PointlessBitwiseExpression")
public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
/**
@@ -177,7 +179,7 @@
@RequiresApi(31)
@NonNull
public ContentInfo toContentInfo() {
- return mCompat.getWrapped();
+ return Objects.requireNonNull(mCompat.getWrapped());
}
@NonNull
@@ -436,6 +438,7 @@
}
}
+ @RequiresApi(31)
private static final class Compat31Impl implements Compat {
@NonNull
private final ContentInfo mWrapped;
diff --git a/core/core/src/main/java/androidx/core/view/DisplayCompat.java b/core/core/src/main/java/androidx/core/view/DisplayCompat.java
index 7593f36..816d397 100644
--- a/core/core/src/main/java/androidx/core/view/DisplayCompat.java
+++ b/core/core/src/main/java/androidx/core/view/DisplayCompat.java
@@ -27,6 +27,7 @@
import android.text.TextUtils;
import android.view.Display;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@@ -167,7 +168,7 @@
// Check the system property for display size.
String displaySize = getSystemProperty(property);
- if (TextUtils.isEmpty(displaySize)) {
+ if (TextUtils.isEmpty(displaySize) || displaySize == null) {
return null;
}
@@ -275,9 +276,9 @@
static boolean isCurrentModeTheLargestMode(@NonNull Display display) {
Display.Mode currentMode = display.getMode();
Display.Mode[] supportedModes = display.getSupportedModes();
- for (int i = 0; i < supportedModes.length; ++i) {
- if (currentMode.getPhysicalHeight() < supportedModes[i].getPhysicalHeight()
- || currentMode.getPhysicalWidth() < supportedModes[i].getPhysicalWidth()) {
+ for (Display.Mode supportedMode : supportedModes) {
+ if (currentMode.getPhysicalHeight() < supportedMode.getPhysicalHeight()
+ || currentMode.getPhysicalWidth() < supportedMode.getPhysicalWidth()) {
return false;
}
}
@@ -343,7 +344,8 @@
ModeCompat(@NonNull Display.Mode mode, boolean isNative) {
Preconditions.checkNotNull(mode, "mode == null, can't wrap a null reference");
// This simplifies the getPhysicalWidth() / getPhysicalHeight functions below
- mPhysicalSize = new Point(mode.getPhysicalWidth(), mode.getPhysicalHeight());
+ mPhysicalSize = new Point(Api23Impl.getPhysicalWidth(mode),
+ Api23Impl.getPhysicalHeight(mode));
mMode = mode;
mIsNative = isNative;
}
@@ -401,6 +403,22 @@
public Display.Mode toMode() {
return mMode;
}
+
+ @RequiresApi(23)
+ static class Api23Impl {
+ private Api23Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static int getPhysicalWidth(Display.Mode mode) {
+ return mode.getPhysicalWidth();
+ }
+
+ @DoNotInline
+ static int getPhysicalHeight(Display.Mode mode) {
+ return mode.getPhysicalHeight();
+ }
+ }
}
}
-
diff --git a/core/core/src/main/java/androidx/core/view/DisplayCutoutCompat.java b/core/core/src/main/java/androidx/core/view/DisplayCutoutCompat.java
index 83c3af9..039a8e0 100644
--- a/core/core/src/main/java/androidx/core/view/DisplayCutoutCompat.java
+++ b/core/core/src/main/java/androidx/core/view/DisplayCutoutCompat.java
@@ -22,11 +22,11 @@
import android.os.Build;
import android.view.DisplayCutout;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.graphics.Insets;
-import androidx.core.os.BuildCompat;
import androidx.core.util.ObjectsCompat;
import java.util.ArrayList;
@@ -41,7 +41,7 @@
*/
public final class DisplayCutoutCompat {
- private final Object mDisplayCutout;
+ private final DisplayCutout mDisplayCutout;
/**
* Creates a DisplayCutout instance.
@@ -52,8 +52,8 @@
* {@link #getBoundingRects()} ()}.
*/
// TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
- public DisplayCutoutCompat(Rect safeInsets, List<Rect> boundingRects) {
- this(SDK_INT >= 28 ? new DisplayCutout(safeInsets, boundingRects) : null);
+ public DisplayCutoutCompat(@Nullable Rect safeInsets, @Nullable List<Rect> boundingRects) {
+ this(SDK_INT >= 28 ? Api28Impl.createDisplayCutout(safeInsets, boundingRects) : null);
}
/**
@@ -81,12 +81,12 @@
private static DisplayCutout constructDisplayCutout(@NonNull Insets safeInsets,
@Nullable Rect boundLeft, @Nullable Rect boundTop, @Nullable Rect boundRight,
@Nullable Rect boundBottom, @NonNull Insets waterfallInsets) {
- if (BuildCompat.isAtLeastR()) {
- return new DisplayCutout(safeInsets.toPlatformInsets(), boundLeft,
- boundTop, boundRight, boundBottom, waterfallInsets.toPlatformInsets());
+ if (SDK_INT >= 30) {
+ return Api30Impl.createDisplayCutout(safeInsets.toPlatformInsets(), boundLeft, boundTop,
+ boundRight, boundBottom, waterfallInsets.toPlatformInsets());
} else if (SDK_INT >= Build.VERSION_CODES.Q) {
- return new DisplayCutout(safeInsets.toPlatformInsets(), boundLeft,
- boundTop, boundRight, boundBottom);
+ return Api29Impl.createDisplayCutout(safeInsets.toPlatformInsets(), boundLeft, boundTop,
+ boundRight, boundBottom);
} else if (SDK_INT >= Build.VERSION_CODES.P) {
final Rect safeInsetRect = new Rect(safeInsets.left, safeInsets.top, safeInsets.right,
safeInsets.bottom);
@@ -103,20 +103,20 @@
if (boundBottom != null) {
boundingRects.add(boundBottom);
}
- return new DisplayCutout(safeInsetRect, boundingRects);
+ return Api28Impl.createDisplayCutout(safeInsetRect, boundingRects);
} else {
return null;
}
}
- private DisplayCutoutCompat(Object displayCutout) {
+ private DisplayCutoutCompat(DisplayCutout displayCutout) {
mDisplayCutout = displayCutout;
}
/** Returns the inset from the top which avoids the display cutout in pixels. */
public int getSafeInsetTop() {
if (SDK_INT >= 28) {
- return ((DisplayCutout) mDisplayCutout).getSafeInsetTop();
+ return Api28Impl.getSafeInsetTop(mDisplayCutout);
} else {
return 0;
}
@@ -125,7 +125,7 @@
/** Returns the inset from the bottom which avoids the display cutout in pixels. */
public int getSafeInsetBottom() {
if (SDK_INT >= 28) {
- return ((DisplayCutout) mDisplayCutout).getSafeInsetBottom();
+ return Api28Impl.getSafeInsetBottom(mDisplayCutout);
} else {
return 0;
}
@@ -134,7 +134,7 @@
/** Returns the inset from the left which avoids the display cutout in pixels. */
public int getSafeInsetLeft() {
if (SDK_INT >= 28) {
- return ((DisplayCutout) mDisplayCutout).getSafeInsetLeft();
+ return Api28Impl.getSafeInsetLeft(mDisplayCutout);
} else {
return 0;
}
@@ -143,7 +143,7 @@
/** Returns the inset from the right which avoids the display cutout in pixels. */
public int getSafeInsetRight() {
if (SDK_INT >= 28) {
- return ((DisplayCutout) mDisplayCutout).getSafeInsetRight();
+ return Api28Impl.getSafeInsetRight(mDisplayCutout);
} else {
return 0;
}
@@ -161,7 +161,7 @@
@NonNull
public List<Rect> getBoundingRects() {
if (SDK_INT >= 28) {
- return ((DisplayCutout) mDisplayCutout).getBoundingRects();
+ return Api28Impl.getBoundingRects(mDisplayCutout);
} else {
return Collections.emptyList();
}
@@ -179,8 +179,8 @@
*/
@NonNull
public Insets getWaterfallInsets() {
- if (BuildCompat.isAtLeastR()) {
- return Insets.toCompatInsets(((DisplayCutout) mDisplayCutout).getWaterfallInsets());
+ if (SDK_INT >= 30) {
+ return Insets.toCompatInsets(Api30Impl.getWaterfallInsets(mDisplayCutout));
} else {
return Insets.NONE;
}
@@ -203,17 +203,88 @@
return mDisplayCutout == null ? 0 : mDisplayCutout.hashCode();
}
+ @NonNull
@Override
public String toString() {
return "DisplayCutoutCompat{" + mDisplayCutout + "}";
}
- static DisplayCutoutCompat wrap(Object displayCutout) {
+ static DisplayCutoutCompat wrap(DisplayCutout displayCutout) {
return displayCutout == null ? null : new DisplayCutoutCompat(displayCutout);
}
- @RequiresApi(api = 28)
+ @RequiresApi(28)
DisplayCutout unwrap() {
- return (DisplayCutout) mDisplayCutout;
+ return mDisplayCutout;
+ }
+
+ @RequiresApi(28)
+ static class Api28Impl {
+ private Api28Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static DisplayCutout createDisplayCutout(Rect safeInsets, List<Rect> boundingRects) {
+ return new DisplayCutout(safeInsets, boundingRects);
+ }
+
+ @DoNotInline
+ static int getSafeInsetTop(DisplayCutout displayCutout) {
+ return displayCutout.getSafeInsetTop();
+ }
+
+ @DoNotInline
+ static int getSafeInsetBottom(DisplayCutout displayCutout) {
+ return displayCutout.getSafeInsetBottom();
+ }
+
+ @DoNotInline
+ static int getSafeInsetLeft(DisplayCutout displayCutout) {
+ return displayCutout.getSafeInsetLeft();
+ }
+
+ @DoNotInline
+ static int getSafeInsetRight(DisplayCutout displayCutout) {
+ return displayCutout.getSafeInsetRight();
+ }
+
+ @DoNotInline
+ static List<Rect> getBoundingRects(DisplayCutout displayCutout) {
+ return displayCutout.getBoundingRects();
+ }
+ }
+
+ @RequiresApi(30)
+ static class Api30Impl {
+ private Api30Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static DisplayCutout createDisplayCutout(android.graphics.Insets safeInsets, Rect boundLeft,
+ Rect boundTop, Rect boundRight, Rect boundBottom,
+ android.graphics.Insets waterfallInsets) {
+ return new DisplayCutout(safeInsets, boundLeft, boundTop, boundRight, boundBottom,
+ waterfallInsets);
+ }
+
+ @DoNotInline
+ static android.graphics.Insets getWaterfallInsets(DisplayCutout displayCutout) {
+ return displayCutout.getWaterfallInsets();
+ }
+ }
+
+ @RequiresApi(29)
+ static class Api29Impl {
+ private Api29Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static DisplayCutout createDisplayCutout(android.graphics.Insets safeInsets, Rect boundLeft,
+ Rect boundTop, Rect boundRight, Rect boundBottom) {
+ return new DisplayCutout(safeInsets, boundLeft, boundTop, boundRight, boundBottom);
+ }
}
}
diff --git a/core/core/src/main/java/androidx/core/view/DragAndDropPermissionsCompat.java b/core/core/src/main/java/androidx/core/view/DragAndDropPermissionsCompat.java
index 6c3393c..2e6469b 100644
--- a/core/core/src/main/java/androidx/core/view/DragAndDropPermissionsCompat.java
+++ b/core/core/src/main/java/androidx/core/view/DragAndDropPermissionsCompat.java
@@ -23,11 +23,14 @@
import android.view.DragAndDropPermissions;
import android.view.DragEvent;
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
/**
- * Helper for accessing features in {@link android.view.DragAndDropPermissions} a backwards
+ * Helper for accessing features in {@link DragAndDropPermissions} a backwards
* compatible fashion.
*
* <p>
@@ -37,19 +40,20 @@
* </p>
*/
public final class DragAndDropPermissionsCompat {
- private Object mDragAndDropPermissions;
+ private final DragAndDropPermissions mDragAndDropPermissions;
- private DragAndDropPermissionsCompat(Object dragAndDropPermissions) {
+ private DragAndDropPermissionsCompat(DragAndDropPermissions dragAndDropPermissions) {
mDragAndDropPermissions = dragAndDropPermissions;
}
/** @hide */
@RestrictTo(LIBRARY_GROUP_PREFIX)
@Nullable
- public static DragAndDropPermissionsCompat request(Activity activity, DragEvent dragEvent) {
+ public static DragAndDropPermissionsCompat request(@NonNull Activity activity,
+ @NonNull DragEvent dragEvent) {
if (Build.VERSION.SDK_INT >= 24) {
DragAndDropPermissions dragAndDropPermissions =
- activity.requestDragAndDropPermissions(dragEvent);
+ Api24Impl.requestDragAndDropPermissions(activity, dragEvent);
if (dragAndDropPermissions != null) {
return new DragAndDropPermissionsCompat(dragAndDropPermissions);
}
@@ -62,7 +66,25 @@
*/
public void release() {
if (Build.VERSION.SDK_INT >= 24) {
- ((DragAndDropPermissions) mDragAndDropPermissions).release();
+ Api24Impl.release(mDragAndDropPermissions);
+ }
+ }
+
+ @RequiresApi(24)
+ static class Api24Impl {
+ private Api24Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static DragAndDropPermissions requestDragAndDropPermissions(Activity activity,
+ DragEvent event) {
+ return activity.requestDragAndDropPermissions(event);
+ }
+
+ @DoNotInline
+ static void release(DragAndDropPermissions dragAndDropPermissions) {
+ dragAndDropPermissions.release();
}
}
}
diff --git a/core/core/src/main/java/androidx/core/view/DragStartHelper.java b/core/core/src/main/java/androidx/core/view/DragStartHelper.java
index 8488846..ed099dc 100644
--- a/core/core/src/main/java/androidx/core/view/DragStartHelper.java
+++ b/core/core/src/main/java/androidx/core/view/DragStartHelper.java
@@ -21,6 +21,8 @@
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.NonNull;
+
/**
* DragStartHelper is a utility class for implementing drag and drop support.
* <p>
@@ -84,7 +86,7 @@
* @param helper The DragStartHelper object which detected the gesture.
* @return True if the listener has started the drag operation, false otherwise.
*/
- boolean onDragStart(View v, DragStartHelper helper);
+ boolean onDragStart(@NonNull View v, @NonNull DragStartHelper helper);
}
/**
@@ -93,7 +95,7 @@
* called explicitly.
* @param view A View
*/
- public DragStartHelper(View view, OnDragStartListener listener) {
+ public DragStartHelper(@NonNull View view, @NonNull OnDragStartListener listener) {
mView = view;
mListener = listener;
}
@@ -125,7 +127,7 @@
* the event.
* @return True if the listener has consumed the event, false otherwise.
*/
- public boolean onTouch(View v, MotionEvent event) {
+ public boolean onTouch(@NonNull View v, @NonNull MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
switch (event.getAction()) {
@@ -166,7 +168,7 @@
* @param v The view that was clicked and held.
* @return true if the callback consumed the long click, false otherwise.
*/
- public boolean onLongClick(View v) {
+ public boolean onLongClick(@NonNull View v) {
return mListener.onDragStart(v, this);
}
@@ -174,22 +176,13 @@
* Compute the position of the touch event that started the drag operation.
* @param point The position of the touch event that started the drag operation.
*/
- public void getTouchPosition(Point point) {
+ public void getTouchPosition(@NonNull Point point) {
point.set(mLastTouchX, mLastTouchY);
}
- private final View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- return DragStartHelper.this.onLongClick(v);
- }
- };
+ private final View.OnLongClickListener mLongClickListener =
+ DragStartHelper.this::onLongClick;
- private final View.OnTouchListener mTouchListener = new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return DragStartHelper.this.onTouch(v, event);
- }
- };
+ private final View.OnTouchListener mTouchListener = DragStartHelper.this::onTouch;
}
diff --git a/core/core/src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java b/core/core/src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java
index 5006791..fa1ff26 100644
--- a/core/core/src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java
+++ b/core/core/src/main/java/androidx/core/view/animation/PathInterpolatorCompat.java
@@ -21,6 +21,10 @@
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
/**
* Helper for creating path-based {@link Interpolator} instances. On API 21 or newer, the
* platform implementation will be used and on older platforms a compatible alternative
@@ -44,9 +48,10 @@
* @param path the {@link Path} to use to make the line representing the {@link Interpolator}
* @return the {@link Interpolator} representing the {@link Path}
*/
- public static Interpolator create(Path path) {
+ @NonNull
+ public static Interpolator create(@NonNull Path path) {
if (Build.VERSION.SDK_INT >= 21) {
- return new PathInterpolator(path);
+ return Api21Impl.createPathInterpolator(path);
}
return new PathInterpolatorApi14(path);
}
@@ -59,9 +64,10 @@
* @param controlY the y coordinate of the quadratic Bezier control point
* @return the {@link Interpolator} representing the quadratic Bezier curve
*/
+ @NonNull
public static Interpolator create(float controlX, float controlY) {
if (Build.VERSION.SDK_INT >= 21) {
- return new PathInterpolator(controlX, controlY);
+ return Api21Impl.createPathInterpolator(controlX, controlY);
}
return new PathInterpolatorApi14(controlX, controlY);
}
@@ -76,11 +82,35 @@
* @param controlY2 the y coordinate of the second control point of the cubic Bezier
* @return the {@link Interpolator} representing the cubic Bezier curve
*/
+ @NonNull
public static Interpolator create(float controlX1, float controlY1,
float controlX2, float controlY2) {
if (Build.VERSION.SDK_INT >= 21) {
- return new PathInterpolator(controlX1, controlY1, controlX2, controlY2);
+ return Api21Impl.createPathInterpolator(controlX1, controlY1, controlX2, controlY2);
}
return new PathInterpolatorApi14(controlX1, controlY1, controlX2, controlY2);
}
+
+ @RequiresApi(21)
+ static class Api21Impl {
+ private Api21Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static PathInterpolator createPathInterpolator(Path path) {
+ return new PathInterpolator(path);
+ }
+
+ @DoNotInline
+ static PathInterpolator createPathInterpolator(float controlX, float controlY) {
+ return new PathInterpolator(controlX, controlY);
+ }
+
+ @DoNotInline
+ static PathInterpolator createPathInterpolator(float controlX1, float controlY1,
+ float controlX2, float controlY2) {
+ return new PathInterpolator(controlX1, controlY1, controlX2, controlY2);
+ }
+ }
}
diff --git a/core/core/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java b/core/core/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
index d83e1cb..3e1acef 100644
--- a/core/core/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
+++ b/core/core/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
@@ -21,7 +21,9 @@
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipDescription;
+import android.content.ContentProvider;
import android.net.Uri;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -29,15 +31,19 @@
import android.util.Log;
import android.view.View;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputContentInfo;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.core.util.ObjectsCompat;
import androidx.core.util.Preconditions;
import androidx.core.view.ContentInfoCompat;
+import androidx.core.view.OnReceiveContentListener;
import androidx.core.view.ViewCompat;
/**
@@ -142,7 +148,7 @@
@NonNull EditorInfo editorInfo, @NonNull InputContentInfoCompat inputContentInfo,
int flags, @Nullable Bundle opts) {
if (Build.VERSION.SDK_INT >= 25) {
- return inputConnection.commitContent(
+ return Api25Impl.commitContent(inputConnection,
(InputContentInfo) inputContentInfo.unwrap(), flags, opts);
} else {
final int protocol = EditorInfoCompat.getProtocol(editorInfo);
@@ -204,16 +210,16 @@
* <p>On API <= 24 devices, IME developers need to ensure that the content URI is accessible
* only from the target application, for example, by generating a URL with a unique name that
* others cannot guess. IME developers can also rely on the following information of the target
- * application to do additional access checks in their {@link android.content.ContentProvider}.
+ * application to do additional access checks in their {@link ContentProvider}.
* </p>
* <ul>
* <li>On API >= 23 {@link EditorInfo#packageName} is guaranteed to not be spoofed, which
- * can later be compared with {@link android.content.ContentProvider#getCallingPackage()} in
- * the {@link android.content.ContentProvider}.
+ * can later be compared with {@link ContentProvider#getCallingPackage()} in
+ * the {@link ContentProvider}.
* </li>
- * <li>{@link android.view.inputmethod.InputBinding#getUid()} is guaranteed to not be
- * spoofed, which can later be compared with {@link android.os.Binder#getCallingUid()} in
- * the {@link android.content.ContentProvider}.</li>
+ * <li>{@link InputBinding#getUid()} is guaranteed to not be
+ * spoofed, which can later be compared with {@link Binder#getCallingUid()} in
+ * the {@link ContentProvider}.</li>
* </ul>
*/
public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 0x00000001;
@@ -232,7 +238,6 @@
* request is already handled or still being handled in background. {@code false} to use the
* default implementation
*/
- @SuppressWarnings("NullableProblems") // Not useful here
boolean onCommitContent(@NonNull InputContentInfoCompat inputContentInfo, int flags,
@Nullable Bundle opts);
}
@@ -334,6 +339,7 @@
*
* @return A wrapper {@link InputConnection} object that can be returned to the IME.
*/
+ @SuppressWarnings("deprecation")
@NonNull
public static InputConnection createWrapper(@NonNull View view,
@NonNull InputConnection inputConnection, @NonNull EditorInfo editorInfo) {
@@ -345,44 +351,40 @@
/**
* Creates an {@link OnCommitContentListener} that uses
* {@link ViewCompat#performReceiveContent} to insert content. This is useful for widgets
- * that support content insertion using an {@link androidx.core.view.OnReceiveContentListener}.
+ * that support content insertion using an {@link OnReceiveContentListener}.
*/
@NonNull
private static OnCommitContentListener createOnCommitContentListenerUsingPerformReceiveContent(
@NonNull View view) {
Preconditions.checkNotNull(view);
- return new OnCommitContentListener() {
- @Override
- public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags,
- Bundle opts) {
- Bundle extras = opts;
- if (Build.VERSION.SDK_INT >= 25
- && (flags & INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
- try {
- inputContentInfo.requestPermission();
- } catch (Exception e) {
- Log.w(LOG_TAG,
- "Can't insert content from IME; requestPermission() failed", e);
- return false;
- }
- // Permissions granted above are revoked automatically by the platform when the
- // corresponding InputContentInfo object is garbage collected. To prevent
- // this from happening prematurely (before the receiving app has had a chance
- // to process the content), we set the InputContentInfo object into the
- // extras of the payload passed to OnReceiveContentListener.
- InputContentInfo inputContentInfoFmk =
- (InputContentInfo) inputContentInfo.unwrap();
- extras = (opts == null) ? new Bundle() : new Bundle(opts);
- extras.putParcelable(EXTRA_INPUT_CONTENT_INFO, inputContentInfoFmk);
+ return (inputContentInfo, flags, opts) -> {
+ Bundle extras = opts;
+ if (Build.VERSION.SDK_INT >= 25
+ && (flags & INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
+ try {
+ inputContentInfo.requestPermission();
+ } catch (Exception e) {
+ Log.w(LOG_TAG,
+ "Can't insert content from IME; requestPermission() failed", e);
+ return false;
}
- ClipData clip = new ClipData(inputContentInfo.getDescription(),
- new ClipData.Item(inputContentInfo.getContentUri()));
- ContentInfoCompat payload = new ContentInfoCompat.Builder(clip, SOURCE_INPUT_METHOD)
- .setLinkUri(inputContentInfo.getLinkUri())
- .setExtras(extras)
- .build();
- return ViewCompat.performReceiveContent(view, payload) == null;
+ // Permissions granted above are revoked automatically by the platform when the
+ // corresponding InputContentInfo object is garbage collected. To prevent
+ // this from happening prematurely (before the receiving app has had a chance
+ // to process the content), we set the InputContentInfo object into the
+ // extras of the payload passed to OnReceiveContentListener.
+ InputContentInfo inputContentInfoFmk =
+ (InputContentInfo) inputContentInfo.unwrap();
+ extras = (opts == null) ? new Bundle() : new Bundle(opts);
+ extras.putParcelable(EXTRA_INPUT_CONTENT_INFO, inputContentInfoFmk);
}
+ ClipData clip = new ClipData(inputContentInfo.getDescription(),
+ new ClipData.Item(inputContentInfo.getContentUri()));
+ ContentInfoCompat payload = new ContentInfoCompat.Builder(clip, SOURCE_INPUT_METHOD)
+ .setLinkUri(inputContentInfo.getLinkUri())
+ .setExtras(extras)
+ .build();
+ return ViewCompat.performReceiveContent(view, payload) == null;
};
}
@@ -399,4 +401,17 @@
@Deprecated
public InputConnectionCompat() {
}
+
+ @RequiresApi(25)
+ static class Api25Impl {
+ private Api25Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static boolean commitContent(InputConnection inputConnection,
+ InputContentInfo inputContentInfo, int i, Bundle bundle) {
+ return inputConnection.commitContent(inputContentInfo, i, bundle);
+ }
+ }
}
diff --git a/core/core/src/test/java/androidx/core/util/SizeFCompatTest.java b/core/core/src/test/java/androidx/core/util/SizeFCompatTest.java
new file mode 100644
index 0000000..cde04f1
--- /dev/null
+++ b/core/core/src/test/java/androidx/core/util/SizeFCompatTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.core.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.annotation.TargetApi;
+import android.util.SizeF;
+
+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
+@Config(sdk = {19, 21})
+public class SizeFCompatTest {
+
+ @Test
+ public void constructor_validSize() {
+ SizeFCompat size = new SizeFCompat(100.4f, 150.2f);
+
+ assertThat(size.getWidth()).isEqualTo(100.4f);
+ assertThat(size.getHeight()).isEqualTo(150.2f);
+ }
+
+ @Test
+ public void constructor_invalidSize() {
+ assertThrows(IllegalArgumentException.class, () -> new SizeFCompat(Float.NaN, 100f));
+ assertThrows(IllegalArgumentException.class, () -> new SizeFCompat(12.5f, Float.NaN));
+ }
+
+ @Test
+ public void equals() {
+ assertThat(new SizeFCompat(100.4f, 150.2f)).isEqualTo(new SizeFCompat(100.4f, 150.2f));
+ assertThat(new SizeFCompat(100.4f, 150.2f)).isNotEqualTo(new SizeFCompat(10.4f, 150.2f));
+ assertThat(new SizeFCompat(100.4f, 150.2f)).isNotEqualTo(new SizeFCompat(100.4f, 15.2f));
+ }
+
+ @Test
+ public void hashCode_sameForEqualSizes() {
+ assertThat(new SizeFCompat(100.4f, 150.2f).hashCode())
+ .isEqualTo(new SizeFCompat(100.4f, 150.2f).hashCode());
+ assertThat(new SizeFCompat(100.4f, 150.2f).hashCode())
+ .isNotEqualTo(new SizeFCompat(10.4f, 150.2f).hashCode());
+ assertThat(new SizeFCompat(100.4f, 150.2f).hashCode())
+ .isNotEqualTo(new SizeFCompat(100.4f, 15.2f).hashCode());
+ }
+
+ @Test
+ public void toString_readable() {
+ assertThat(new SizeFCompat(10.2f, 20.4f).toString()).isEqualTo("10.2x20.4");
+ }
+
+ @TargetApi(21)
+ @Config(sdk = 21)
+ @Test
+ public void toSizeF() {
+ assertThat(new SizeFCompat(10.2f, 20.4f).toSizeF()).isEqualTo(new SizeF(10.2f, 20.4f));
+ }
+
+ @TargetApi(21)
+ @Config(sdk = 21)
+ @Test
+ public void createFromSizeF() {
+ assertThat(SizeFCompat.createFromSizeF(new SizeF(11.2f, 21.4f)))
+ .isEqualTo(new SizeFCompat(11.2f, 21.4f));
+ }
+}
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index ecc237e6..635400e3 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -133,7 +133,6 @@
\- Support for Scala 3
For more details see .*
Daemon will be stopped at the end of the build
-# > Task :buildSrc:build
# > Configure project :appsearch:appsearch\-local\-backend
Configuration on demand is an incubating feature\.
Configuration cache is an incubating feature\.
@@ -177,9 +176,6 @@
Note: Recompile with \-Xlint:removal for details\.
Note\: Some input files use unchecked or unsafe operations\.
Note\: Recompile with \-Xlint\:unchecked for details\.
-# > Task :benchmark:benchmark-junit4:processDebugAndroidTestManifest
-# > Task :benchmark:benchmark-common:processDebugAndroidTestManifest
-# > Task :tracing:tracing:compileDebugAndroidTestJavaWithJavac
# > Task :ui:ui-tooling:processDebugAndroidTestManifest
application@android:debuggable was tagged at .*\.xml:[0-9]+ to replace other declarations but no other declaration present
\$OUT_DIR/androidx/wear/compose/compose\-material\-benchmark/build/intermediates/tmp/manifest/androidTest/release/tempFile[0-9]+ProcessTestManifest[0-9]+\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
@@ -208,7 +204,6 @@
\$OUT_DIR/androidx/compose/ui/ui\-graphics/ui\-graphics\-benchmark/build/intermediates/tmp/manifest/androidTest/release/tempFile[0-9]+ProcessTestManifest[0-9]+\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
# > Task :compose:foundation:foundation-layout:processDebugAndroidTestManifest
\$SUPPORT/compose/foundation/foundation\-layout/src/androidAndroidTest/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
-# > Task :compose:navigation:navigation:processDebugAndroidTestManifest
# > Task :compose:runtime:runtime-saveable:processDebugAndroidTestManifest
\$SUPPORT/compose/runtime/runtime\-saveable/src/androidAndroidTest/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
# > Task :compose:runtime:runtime:benchmark:processReleaseAndroidTestManifest
@@ -229,8 +224,8 @@
location\: class PendingIntent
\$OUT_DIR\/androidx\/docs\-public\/build\/unzippedDocsSources\/androidx\/work\/impl\/utils\/ForceStopRunnable\.java\:[0-9]+\: error\: cannot find symbol
# > Task :buildOnServer
+[0-9]+ problems were found reusing the configuration cache\.
[0-9]+ problems were found reusing the configuration cache, [0-9]+ of which seem unique\.
-\- Task `:docs\-tip\-of\-tree:unzipSampleSources` of type `org\.gradle\.api\.tasks\.Sync`: cannot deserialize object of type 'org\.gradle\.api\.Project' as these are not supported with the configuration cache\.
[0-9]+ actionable tasks: [0-9]+ executed, [0-9]+ up\-to\-date
Configuration cache entry reused with [0-9]+ problems\.
See the profiling report at: file://\$GRADLE_USER_HOME/daemon/.*/reports/profile/profile\-[0-9]+\-[0-9]+\-[0-9]+\-[0-9]+\-[0-9]+\-[0-9]+\.html
@@ -245,19 +240,8 @@
# > Task :lifecycle:lifecycle-common:compileJava
Note: \$[^ ]+ uses or overrides a deprecated API\.
Note: Recompile with \-Xlint\:deprecation for details\.
-# > Task :concurrent:concurrent-futures:compileJava
-# > Task :contentpager:contentpager:compileReleaseJavaWithJavac
-# > Task :coordinatorlayout:coordinatorlayout:compileReleaseJavaWithJavac
-# > Task :customview:customview:compileReleaseJavaWithJavac
-# > Task :recommendation:recommendation:compileReleaseJavaWithJavac
-# > Task :textclassifier:textclassifier:compileReleaseJavaWithJavac
# > Task :publicDocsTask
[0-9]+ warnings
-# > Task :support-animation-demos:compileDebugJavaWithJavac
-# > Task :lint-demos:lint-demo-appcompat:compileDebugJavaWithJavac
-# > Task :support-transition-demos:compileDebugJavaWithJavac
-# > Task :support-content-demos:compileDebugJavaWithJavac
-# > Task :support-preference-demos:compileDebugJavaWithJavac
# > Task :startup:integration-tests:first-library:processDebugManifest
\$SUPPORT/startup/integration\-tests/first\-library/src/main/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
meta\-data\#androidx\.work\.WorkManagerInitializer was tagged at AndroidManifest\.xml\:[0-9]+ to remove other declarations but no other declaration present
@@ -265,23 +249,12 @@
Note: \$SUPPORT/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser\.java uses unchecked or unsafe operations\.
# > Task :emoji2:emoji2-benchmark:processReleaseAndroidTestManifest
\$SUPPORT/emoji[0-9]+/emoji[0-9]+\-benchmark/src/androidTest/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
-# > Task :gridlayout:gridlayout:compileDebugAndroidTestJavaWithJavac
-# > Task :leanback:leanback-tab:processDebugAndroidTestManifest
-# > Task :lifecycle:lifecycle-extensions:processDebugAndroidTestManifest
# > Task :leanback:leanback:compileDebugAndroidTestJavaWithJavac
reason: class file for kotlin\.annotation\.AnnotationTarget not found
reason: class file for kotlin\.annotation\.AnnotationRetention not found
warning: unknown enum constant AnnotationTarget\.ANNOTATION_CLASS
-# > Task :navigation:navigation-dynamic-features-runtime:processDebugAndroidTestManifest
-# > Task :mediarouter:mediarouter:compileDebugAndroidTestJavaWithJavac
-# > Task :palette:palette:processDebugAndroidTestManifest
-# > Task :percentlayout:percentlayout:processDebugAndroidTestManifest
-# > Task :preference:preference-ktx:processDebugAndroidTestManifest
# > Task :recyclerview:recyclerview-benchmark:processReleaseAndroidTestManifest
\$SUPPORT/recyclerview/recyclerview\-benchmark/src/androidTest/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
-# > Task :remotecallback:remotecallback:processDebugAndroidTestManifest
-# > Task :recyclerview:recyclerview-selection:compileDebugAndroidTestJavaWithJavac
-# > Task :savedstate:savedstate:processDebugAndroidTestManifest
# > Task :room:room-benchmark:processReleaseAndroidTestManifest
\$SUPPORT/room/benchmark/src/androidTest/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
# > Task :room:room-benchmark:kaptReleaseAndroidTestKotlin
@@ -291,7 +264,6 @@
\$SUPPORT/room/integration\-tests/noappcompattestapp/src/androidTest/java/androidx/room/integration/noappcompat/BareRelationDatabaseTest\.java:[0-9]+: warning: The return value includes a POJO with a @Relation\. It is usually desired to annotate this method with @Transaction to avoid possibility of inconsistent results between the POJO and its relations\. See https://developer\.android\.com/reference/androidx/room/Transaction\.html for details\.
UserAndPets getUserWithPets\(long id\);
List<UserAndPet> getUsersWithPet\(\);
-# > Task :textclassifier:integration-tests:testapp:compileDebugAndroidTestJavaWithJavac
# > Task :room:integration-tests:room-testapp:compileDebugAndroidTestJavaWithJavac
Note: \$SUPPORT/room/integration\-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestUtil\.java uses unchecked or unsafe operations\.
# > Task :activity:integration-tests:testapp:processDebugAndroidTestManifest
@@ -312,16 +284,7 @@
# > Task :compose:material:material:icons:generator:zipHtmlResultsOfTest
Html results of .* zipped into.*\.zip
[0-9]+ problems were found storing the configuration cache, [0-9]+ of which seem unique\.
-\- Task `:docs\-tip\-of\-tree:unzipSampleSources` of type `org\.gradle\.api\.tasks\.Sync`: cannot serialize object of type 'org\.gradle\.api\.internal\.artifacts\.configurations\.DefaultConfiguration', a subtype of 'org\.gradle\.api\.artifacts\.Configuration', as these are not supported with the configuration cache\.
See https://docs\.gradle\.org/[0-9]+\.[0-9]+.*/userguide/configuration_cache\.html\#config_cache:requirements:disallowed_types
-\- Task `:docs\-tip\-of\-tree:unzipSampleSources` of type `org\.gradle\.api\.tasks\.Sync`: cannot deserialize object of type 'org\.gradle\.api\.artifacts\.Configuration' as these are not supported with the configuration cache\.
-\- Task `:docs\-tip\-of\-tree:unzipSampleSources` of type `org\.gradle\.api\.tasks\.Sync`: value 'file collection' is not assignable to 'org\.gradle\.api\.artifacts\.Configuration'
-\- Task `:docs\-tip\-of\-tree:unzipSourcesForDackka` of type `org\.gradle\.api\.tasks\.Sync`: cannot deserialize object of type 'org\.gradle\.api\.Project' as these are not supported with the configuration cache\.
-\- Task `:docs\-tip\-of\-tree:unzipSourcesForDackka` of type `org\.gradle\.api\.tasks\.Sync`: cannot deserialize object of type 'org\.gradle\.api\.artifacts\.Configuration' as these are not supported with the configuration cache\.
-\- Task `:docs\-tip\-of\-tree:unzipSourcesForDackka` of type `org\.gradle\.api\.tasks\.Sync`: value 'file collection' is not assignable to 'org\.gradle\.api\.artifacts\.Configuration'
-\- Task `:docs\-tip\-of\-tree:unzipDocsSources` of type `org\.gradle\.api\.tasks\.Sync`: cannot deserialize object of type 'org\.gradle\.api\.artifacts\.Configuration' as these are not supported with the configuration cache\.
-\- Task `:docs\-tip\-of\-tree:unzipDocsSources` of type `org\.gradle\.api\.tasks\.Sync`: value 'file collection' is not assignable to 'org\.gradle\.api\.artifacts\.Configuration'
-\- Task `:docs\-tip\-of\-tree:unzipSampleSources` of type `org\.gradle\.api\.tasks\.Sync`: cannot serialize object of type 'org\.gradle\.api\.internal\.project\.DefaultProject', a subtype of 'org\.gradle\.api\.Project', as these are not supported with the configuration cache\.
[0-9]+ problem was found storing the configuration cache.
\- Task `:listTaskOutputs` of type `androidx\.build\.ListTaskOutputsTask`: invocation of 'Task\.project' at execution time is unsupported\.
\- Task `[^ ]*checkExternalLicenses` of type `[^ ]*CheckExternalDependencyLicensesTask`: invocation of 'Task\.project' at execution time is unsupported\.
@@ -390,7 +353,6 @@
w\: \$SUPPORT\/viewpager[0-9]+\/viewpager[0-9]+\/src\/androidTest\/java\/androidx\/viewpager[0-9]+\/widget\/OnApplyWindowInsetsListenerTest\.kt\: \([0-9]+\, [0-9]+\)\: Unnecessary non\-null assertion \(\!\!\) on a non\-null receiver of type WindowInsetsCompat
w\: \$SUPPORT\/viewpager[0-9]+\/viewpager[0-9]+\/src\/androidTest\/java\/androidx\/viewpager[0-9]+\/widget\/OnApplyWindowInsetsListenerTest\.kt\: \([0-9]+\, [0-9]+\)\: \'consumeStableInsets\(\)\: WindowInsetsCompat\' is deprecated\. Deprecated in Java
w\: \$SUPPORT\/viewpager[0-9]+\/viewpager[0-9]+\/src\/androidTest\/java\/androidx\/viewpager[0-9]+\/widget\/OnApplyWindowInsetsListenerTest\.kt\: \([0-9]+\, [0-9]+\)\: \'consumeDisplayCutout\(\)\: WindowInsetsCompat\' is deprecated\. Deprecated in Java
-# > Task :contentpager:contentpager:compileDebugAndroidTestJavaWithJavac
# > Task :docs-public:dokkaKotlinDocs
No documentation for .*
Found an unresolved type in androidx\.compose\.runtime\.snapshots\.SnapshotStateList\$add\(androidx\.compose\.runtime\.snapshots\.SnapshotStateList\.T\) \(SnapshotStateList\.kt:[0-9]+\)
@@ -597,39 +559,8 @@
\$SUPPORT/compose/ui/ui\-tooling/src/androidAndroidTest/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
No issues found.*
dagger\.lint\.DaggerIssueRegistry in .*/lint\.jar does not specify a vendor; see IssueRegistry#vendor
-# > Task :camera:camera-camera2-pipe:reportLibraryMetrics
-Info: Stripped invalid locals information from [0-9]+ methods\.
-Info: Methods with invalid locals information:
-java\.lang\.Object androidx\.glance\.appwidget\.GlanceAppWidget\.compose\$glance_appwidget_release\(android\.content\.Context\, android\.appwidget\.AppWidgetManager\, int\, java\.lang\.Object\, android\.os\.Bundle\, kotlin\.coroutines\.Continuation\)
-java\.lang\.Object androidx\.glance\.state\.GlanceState\.getDataStore\(android\.content\.Context, androidx\.glance\.state\.GlanceStateDefinition, java\.lang\.String, kotlin\.coroutines\.Continuation\)
-java\.lang\.Object androidx\.wear\.complications\.ComplicationDataSourceInfoRetriever\.retrievePreviewComplicationData\(android\.content\.ComponentName, androidx\.wear\.complications\.data\.ComplicationType, kotlin\.coroutines\.Continuation\)
-java\.lang\.Object androidx\.wear\.watchface\.editor\.BaseEditorSession\.openComplicationDataSourceChooser\$suspendImpl\(androidx\.wear\.watchface\.editor\.BaseEditorSession, int, kotlin\.coroutines\.Continuation\)
-java\.lang\.Object androidx\.compose\.ui\.platform\.GlobalSnapshotManager\$ensureStarted\$[0-9]+\.invokeSuspend\(java\.lang\.Object\)
-java\.lang\.Object androidx\.wear\.watchface\.editor\.BaseEditorSession\$fetchComplicationsData\$[0-9]+\.invokeSuspend\(java\.lang\.Object\)
-java\.lang\.Object androidx\.camera\.camera[0-9]+\.pipe\.CameraDevicesKt\$find\$[0-9]+\.invokeSuspend\(java\.lang\.Object\)
-java\.lang\.Object androidx\.camera\.camera[0-9]+\.pipe\.compat\.VirtualCameraManager\$requestLoop\$[0-9]+\.invokeSuspend\(java\.lang\.Object\)
-Information in locals\-table is invalid with respect to the stack map table\. Local refers to non\-present stack map type for register: [0-9]+ with constraint OBJECT\.
-java\.lang\.Object androidx\.camera\.camera[0-9]+\.pipe\.compat\.VirtualCameraManager\.openCameraWithRetry\-RzXb[0-9]+QE\(java\.lang\.String, kotlinx\.coroutines\.CoroutineScope, kotlin\.coroutines\.Continuation\)
-Information in locals\-table is invalid with respect to the stack map table\. Local refers to non\-present stack map type for register: [0-9]+ with constraint INT\.
-Info: Some warnings are typically a sign of using an outdated Java toolchain\. To fix, recompile the source with an updated toolchain\.
-# > Task :paging:paging-samples:reportLibraryMetrics
-java\.lang\.Object androidx\.paging\.samples\.RemoteMediatorSampleKt\$remoteMediatorItemKeyedSample\$ExampleRemoteMediator\.load\(androidx\.paging\.LoadType, androidx\.paging\.PagingState, kotlin\.coroutines\.Continuation\)
-java\.lang\.Object androidx\.paging\.samples\.RemoteMediatorSampleKt\$remoteMediatorPageKeyedSample\$ExampleRemoteMediator\.load\(androidx\.paging\.LoadType, androidx\.paging\.PagingState, kotlin\.coroutines\.Continuation\)
-# > Task :wear:wear-complications-data:reportLibraryMetrics
-Info: Stripped invalid locals information from [0-9]+ method\.
-# > Task :wear:wear-watchface-editor:reportLibraryMetrics
-java\.lang\.Object androidx\.wear\.watchface\.editor\.BaseEditorSession\.getPreviewData\$wear_watchface_editor_release\(androidx\.wear\.complications\.ComplicationDataSourceInfoRetriever, androidx\.wear\.complications\.ComplicationDataSourceInfo, kotlin\.coroutines\.Continuation\)
-# > Task :work:work-runtime-ktx:reportLibraryMetrics
-java\.lang\.Object androidx\.work\.OperationKt\.await\(androidx\.work\.Operation, kotlin\.coroutines\.Continuation\)
-# > Task :compose:material:material:reportLibraryMetrics
-java\.lang\.Object androidx\.compose\.material\.SnackbarHostState\.showSnackbar\(java\.lang\.String, java\.lang\.String, androidx\.compose\.material\.SnackbarDuration, kotlin\.coroutines\.Continuation\)
# > Task :compose:foundation:foundation:androidReleaseSourcesJar
Encountered duplicate path "android[a-zA-Z]*/.+" during copy operation configured with DuplicatesStrategy\.WARN
-# > Task :camera:camera-camera2-pipe:reportLibraryMetrics
-java\.lang\.Object androidx\.camera\.camera[0-9]+\.pipe\.compat\.Camera[0-9]+DeviceCache\$getCameras\$[0-9]+\.invokeSuspend\(java\.lang\.Object\)
-Type information in locals\-table is inconsistent\. Cannot constrain type: @Nullable androidx\.camera\.camera[0-9]+\.pipe\.core\.Debug \{\} for value: v[0-9]+\(this_\$iv\$iv\) by constraint INT\.
-# > Task :compose:foundation:foundation:integration-tests:foundation-demos:reportLibraryMetrics
-java\.lang\.Object androidx\.compose\.foundation\.demos\.ListDemosKt\$ListHoistedStateDemo\$[0-9]+\$[0-9]+\$[0-9]+\$[0-9]+\$[0-9]+\.invokeSuspend\(java\.lang\.Object\)
# > Task :profileinstaller:profileinstaller-benchmark:processReleaseAndroidTestManifest
\$SUPPORT/profileinstaller/profileinstaller\-benchmark/src/androidTest/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
# > Task :wear:compose:compose-material-benchmark:processReleaseAndroidTestManifest
@@ -675,3 +606,11 @@
C/C\+\+: (debug|release)\|(x86_64|x86|arm64\-v8a|armeabi\-v7a) :Packaging for\: (amd\-[0-9]+|armhf\-[0-9]+|x86\-[0-9]+)
C/C\+\+: (debug|release)\|(x86_64|x86|arm64\-v8a|armeabi\-v7a) : CMakeLists\.txt:[0-9]+ \(PROJECT\)
C/C\+\+: (debug|release)\|(x86_64|x86|arm64\-v8a|armeabi\-v7a) :Compiling for ARM
+# > Task :glance:glance:reportLibraryMetrics
+Info: Stripped invalid locals information from [0-9]+ method\.
+Info: Methods with invalid locals information:
+java\.lang\.Object androidx\.glance\.state\.GlanceState\.getDataStore\(android\.content\.Context, androidx\.glance\.state\.GlanceStateDefinition, java\.lang\.String, kotlin\.coroutines\.Continuation\)
+Information in locals\-table is invalid with respect to the stack map table\. Local refers to non\-present stack map type for register: [0-9]+ with constraint OBJECT\.
+Info: Some warnings are typically a sign of using an outdated Java toolchain\. To fix, recompile the source with an updated toolchain\.
+# > Task :compose:ui:ui-inspection:buildCMakeRelWithDebInfo[arm64-v8a]
+C/C\+\+: ninja: warning: bad deps log signature or version; starting over
\ No newline at end of file
diff --git a/development/build_log_simplifier/update.sh b/development/build_log_simplifier/update.sh
index ef8ba07..e5b3a20 100755
--- a/development/build_log_simplifier/update.sh
+++ b/development/build_log_simplifier/update.sh
@@ -54,7 +54,12 @@
else
logName="gradle.${i}.log"
fi
- if fetch_artifact --bid "$buildId" --target "$target" "logs/$logName"; then
+ filepath="logs/$logName"
+ # incremental build uses a subdirectory
+ if [ "$target" == "androidx_incremental" ]; then
+ filepath="incremental/$filepath"
+ fi
+ if fetch_artifact --bid "$buildId" --target "$target" "$filepath"; then
echo "downloaded log ${i} in build $buildId target $target"
else
echo
diff --git a/development/importMaven/build.gradle.kts b/development/importMaven/build.gradle.kts
index d8a863e..5e20af1 100644
--- a/development/importMaven/build.gradle.kts
+++ b/development/importMaven/build.gradle.kts
@@ -461,12 +461,18 @@
}
withFiles {
addFile("${id.name}-${id.version}.pom")
+ addFile("${id.name}-${id.version}.pom.asc")
addFile("${id.name}-${id.version}.module")
+ addFile("${id.name}-${id.version}.module.asc")
addFile("${id.name}-${id.version}.jar")
+ addFile("${id.name}-${id.version}.jar.asc")
addFile("${id.name}-${id.version}.aar")
+ addFile("${id.name}-${id.version}.aar.asc")
addFile("${id.name}-${id.version}-sources.jar")
addFile("${id.name}-${id.version}.klib")
+ addFile("${id.name}-${id.version}.klib.asc")
addFile("${id.name}-${id.version}-cinterop-interop.klib")
+ addFile("${id.name}-${id.version}-cinterop-interop.klib.asc")
}
}
}
diff --git a/development/project-creator/create_project.py b/development/project-creator/create_project.py
index 7501453..ae4b191 100755
--- a/development/project-creator/create_project.py
+++ b/development/project-creator/create_project.py
@@ -745,6 +745,10 @@
project_type = ProjectType.KOTLIN
else:
project_type = ask_project_type()
+ insert_new_group_id_into_library_versions_kt(
+ args.group_id,
+ args.artifact_id
+ )
create_directories(
args.group_id,
args.artifact_id,
@@ -752,8 +756,6 @@
is_compose_project(args.group_id, args.artifact_id)
)
update_settings_gradle(args.group_id, args.artifact_id)
- insert_new_group_id_into_library_versions_kt(args.group_id,
- args.artifact_id)
update_docs_tip_of_tree_build_grade(args.group_id, args.artifact_id)
print("Created directories. \nRunning updateApi for the new "
"library, this may take a minute...", end='')
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 6b5e28f..162e164 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -31,13 +31,13 @@
docs("androidx.biometric:biometric-ktx:1.2.0-alpha04")
samples("androidx.biometric:biometric-ktx-samples:1.2.0-alpha04")
docs("androidx.browser:browser:1.4.0")
- docs("androidx.camera:camera-camera2:1.1.0-alpha10")
- docs("androidx.camera:camera-core:1.1.0-alpha10")
- docs("androidx.camera:camera-extensions:1.0.0-alpha30")
+ docs("androidx.camera:camera-camera2:1.1.0-alpha11")
+ docs("androidx.camera:camera-core:1.1.0-alpha11")
+ docs("androidx.camera:camera-extensions:1.0.0-alpha31")
stubs(fileTree(dir: "../camera/camera-extensions-stub", include: ["camera-extensions-stub.jar"]))
- docs("androidx.camera:camera-lifecycle:1.1.0-alpha10")
- docs("androidx.camera:camera-video:1.1.0-alpha10")
- docs("androidx.camera:camera-view:1.0.0-alpha30")
+ docs("androidx.camera:camera-lifecycle:1.1.0-alpha11")
+ docs("androidx.camera:camera-video:1.1.0-alpha11")
+ docs("androidx.camera:camera-view:1.0.0-alpha31")
docs("androidx.car.app:app:1.2.0-alpha01")
docs("androidx.car.app:app-automotive:1.2.0-alpha01")
docs("androidx.car.app:app-projected:1.2.0-alpha01")
@@ -92,7 +92,7 @@
docs("androidx.concurrent:concurrent-futures:1.1.0")
docs("androidx.concurrent:concurrent-futures-ktx:1.1.0")
docs("androidx.contentpager:contentpager:1.0.0")
- docs("androidx.coordinatorlayout:coordinatorlayout:1.2.0-alpha01")
+ docs("androidx.coordinatorlayout:coordinatorlayout:1.2.0-beta01")
docs("androidx.core:core-google-shortcuts:1.1.0-alpha02")
docs("androidx.core:core-role:1.1.0-alpha02")
docs("androidx.core:core-animation:1.0.0-alpha02")
@@ -227,7 +227,7 @@
docs("androidx.slice:slice-builders-ktx:1.0.0-alpha08")
docs("androidx.slice:slice-core:1.1.0-alpha02")
docs("androidx.slice:slice-view:1.1.0-alpha02")
- docs("androidx.slidingpanelayout:slidingpanelayout:1.2.0-beta01")
+ docs("androidx.slidingpanelayout:slidingpanelayout:1.2.0-rc01")
docs("androidx.sqlite:sqlite:2.2.0-beta02")
docs("androidx.sqlite:sqlite-framework:2.2.0-beta02")
docs("androidx.sqlite:sqlite-ktx:2.2.0-beta02")
@@ -278,13 +278,13 @@
samples("androidx.wear:wear-input-samples:1.2.0-alpha01")
docs("androidx.wear:wear-input-testing:1.2.0-alpha02")
docs("androidx.webkit:webkit:1.4.0")
- docs("androidx.window:window:1.0.0-beta03")
+ docs("androidx.window:window:1.0.0-beta04")
stubs(fileTree(dir: "../window/stubs/", include: ["window-sidecar-release-0.1.0-alpha01.aar"]))
stubs("androidx.window:window-extensions:1.0.0-alpha01")
- docs("androidx.window:window-java:1.0.0-beta03")
- docs("androidx.window:window-rxjava2:1.0.0-beta03")
- docs("androidx.window:window-rxjava3:1.0.0-beta03")
- docs("androidx.window:window-testing:1.0.0-beta03")
+ docs("androidx.window:window-java:1.0.0-beta04")
+ docs("androidx.window:window-rxjava2:1.0.0-beta04")
+ docs("androidx.window:window-rxjava3:1.0.0-beta04")
+ docs("androidx.window:window-testing:1.0.0-beta04")
docs("androidx.work:work-gcm:2.7.1")
docs("androidx.work:work-multiprocess:2.7.1")
docs("androidx.work:work-runtime:2.7.1")
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index ab90b85..8b81fb7 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -101,6 +101,7 @@
docs(project(":core:core-google-shortcuts"))
docs(project(":core:core-ktx"))
docs(project(":core:core-performance"))
+ samples(project(":core:core-performance:core-performance-samples"))
docs(project(":core:core-remoteviews"))
docs(project(":core:core-splashscreen"))
docs(project(":core:core-role"))
diff --git a/docs/api_guidelines.md b/docs/api_guidelines.md
index 315585a2..bfeeb4e 100644
--- a/docs/api_guidelines.md
+++ b/docs/api_guidelines.md
@@ -53,7 +53,7 @@
```
<feature-name>/
<feature-name>-<sub-feature>/ [<feature-name>:<feature-name>-<sub-feature>]
- samples/ [<feature-name>:<feature-name>-<sub-feature>:samples]
+ samples/ [<feature-name>:<feature-name>-<sub-feature>-samples]
integration-tests/
testapp/ [<feature-name>:testapp]
testlib/ [<feature-name>:testlib]
@@ -101,6 +101,8 @@
* `-ktx` for an Kotlin artifact that exposes idiomatic Kotlin APIs as an
extension to a Java-only library (see
[additional -ktx guidance](#module-ktx))
+* `-samples` for sample code which can be inlined in documentation (see
+ [Sample code in Kotlin modules](#sample-code-in-kotlin-modules)
* `-<third-party>` for an artifact that integrates an optional third-party API
surface, e.g. `-proto` or `-rxjava2`. Note that a major version is included
in the sub-feature name for third-party API surfaces where the major version
@@ -151,7 +153,7 @@
library at the same time and release all libraries at the same time.
Atomic groups are specified in
-[`LibraryGroups.kt`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt):
+[`LibraryGroups.kt`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/public/src/main/kotlin/androidx/build/LibraryGroups.kt):
```kotlin
// Non-atomic library group
@@ -275,13 +277,14 @@
* Superclass **must** be `Object`
* Class **must** be non-instantiable, i.e. constructor is private no-op
* Static fields and static methods **must** match match signatures with
- `PlatformClass`
+ `<PlatformClass>`
* Static fields that can be inlined, ex. integer constants, **must not**
be shimmed
* Public method names **must** match platform method names
-* Public methods **must** be static and take `PlatformClass` as first
- parameter
-* Implementation *may* delegate to `PlatformClass` methods when available
+* Public methods **must** be static and take `<PlatformClass>` as first
+ parameter (except in the case of static methods on the platform class, as
+ shown below)
+* Implementation *may* delegate to `<PlatformClass>` methods when available
#### Sample {#static-shim-sample}
@@ -457,12 +460,14 @@
// loading and prevent optimization.
@RequiresApi(23)
private static class Api23Impl {
+ @DoNotInline
@NonNull
static ModemInfo create() {
return new ModemInfo();
}
- static boolean isLteSupported(PlatformClass obj) {
+ @DoNotInline
+ static boolean isLteSupported(ModemInfo obj) {
return obj.isLteSupported();
}
}
@@ -2152,7 +2157,8 @@
The follow demonstrates how to reference sample functions from public API. It is
also recommended to reuse these samples in unit tests / integration tests / test
-apps / library demos where possible.
+apps / library demos where possible to help ensure that the samples work as
+intended.
**Public API:**
@@ -2178,7 +2184,7 @@
}
```
-**Generated documentation visible on d.android.com\***
+**Generated documentation visible on d.android.com / within Android Studio**
```
fun fancyPrint(str: String)
@@ -2192,16 +2198,37 @@
<code>
```
-\**still some improvements to be made to DAC side, such as syntax highlighting*
+Warning: Only the body of the function is used in generated documentation, so
+any other references to elements defined outside the body of the function (such
+as variables defined within the sample file) will not be visible. To ensure that
+samples can be easily copy and pasted without errors, make sure that any
+references are defined within the body of the function.
### Module configuration
-The following module setups should be used for sample functions, and are
-enforced by lint:
+The following module setups should be used for sample functions:
+
+**Per-module samples**
+
+For library groups with relatively independent sub-libraries. This is the
+recommended project setup, and should be used in most cases.
+
+Gradle project name: `:foo-library:foo-module:foo-module-samples`
+
+```
+foo-library/
+ foo-module/
+ samples/
+```
**Group-level samples**
-For library groups with strongly related samples that want to share code.
+For library groups with strongly related samples that want to share code and be
+reused across a library group, a singular shared samples library can be created.
+In most cases this is discouraged - samples should be small and show the usage
+of a particular API / small set of APIs, instead of more complicated usage
+combining multiple APIs from across libraries. For these cases a sample
+application is more appropriate.
Gradle project name: `:foo-library:foo-library-samples`
@@ -2212,22 +2239,10 @@
samples/
```
-**Per-module samples**
-
-For library groups with complex, relatively independent sub-libraries
-
-Gradle project name: `:foo-library:foo-module:foo-module-samples`
-
-```
-foo-library/
- foo-module/
- samples/
-```
-
**Samples module configuration**
Samples modules are published to GMaven so that they are available to Android
-Studio, which displays code in @Sample annotations as hover-over pop-ups.
+Studio, which displays referenced samples as hover-over pop-ups.
To achieve this, samples modules must declare the same MavenGroup and `publish`
as the library(s) they are samples for.
diff --git a/docs/benchmarking_images/filter_build.png b/docs/benchmarking_images/filter_build.png
deleted file mode 100644
index f4d15d4..0000000
--- a/docs/benchmarking_images/filter_build.png
+++ /dev/null
Binary files differ
diff --git a/docs/benchmarking_images/filter_initial.png b/docs/benchmarking_images/filter_initial.png
index 61bdc30..b538051 100644
--- a/docs/benchmarking_images/filter_initial.png
+++ b/docs/benchmarking_images/filter_initial.png
Binary files differ
diff --git a/docs/benchmarking_images/filter_metric.png b/docs/benchmarking_images/filter_metric.png
new file mode 100644
index 0000000..0e714fd
--- /dev/null
+++ b/docs/benchmarking_images/filter_metric.png
Binary files differ
diff --git a/docs/benchmarking_images/filter_test.png b/docs/benchmarking_images/filter_test.png
index 9a8a0ee..cfc8a28 100644
--- a/docs/benchmarking_images/filter_test.png
+++ b/docs/benchmarking_images/filter_test.png
Binary files differ
diff --git a/docs/benchmarking_images/triage_bug.png b/docs/benchmarking_images/triage_bug.png
deleted file mode 100644
index 816122c..0000000
--- a/docs/benchmarking_images/triage_bug.png
+++ /dev/null
Binary files differ
diff --git a/docs/benchmarking_images/triage_cl_list.png b/docs/benchmarking_images/triage_cl_list.png
deleted file mode 100644
index d65a7e2..0000000
--- a/docs/benchmarking_images/triage_cl_list.png
+++ /dev/null
Binary files differ
diff --git a/docs/benchmarking_images/triage_complete.png b/docs/benchmarking_images/triage_complete.png
deleted file mode 100644
index 3a04763..0000000
--- a/docs/benchmarking_images/triage_complete.png
+++ /dev/null
Binary files differ
diff --git a/docs/benchmarking_images/triage_graph.png b/docs/benchmarking_images/triage_graph.png
deleted file mode 100644
index 90defbc..0000000
--- a/docs/benchmarking_images/triage_graph.png
+++ /dev/null
Binary files differ
diff --git a/docs/benchmarking_images/triage_initial.png b/docs/benchmarking_images/triage_initial.png
deleted file mode 100644
index ea0d033..0000000
--- a/docs/benchmarking_images/triage_initial.png
+++ /dev/null
Binary files differ
diff --git a/docs/benchmarking_images/triage_regression.png b/docs/benchmarking_images/triage_regression.png
deleted file mode 100644
index bed4b5f..0000000
--- a/docs/benchmarking_images/triage_regression.png
+++ /dev/null
Binary files differ
diff --git a/docs/benchmarking_images/triage_word_cloud.png b/docs/benchmarking_images/triage_word_cloud.png
deleted file mode 100644
index 35dbe43..0000000
--- a/docs/benchmarking_images/triage_word_cloud.png
+++ /dev/null
Binary files differ
diff --git a/docs/gradle_enterprise.md b/docs/gradle_enterprise.md
new file mode 100644
index 0000000..49484441b
--- /dev/null
+++ b/docs/gradle_enterprise.md
@@ -0,0 +1,22 @@
+# Gradle Enterprise
+
+<!--*
+# Document freshness: For more information, see go/fresh-source.
+freshness: { owner: 'rahulrav' reviewed: '2021-11-03' }
+*-->
+
+[TOC]
+
+## Introduction
+
+Gradle Enterprise is used to speed up workflows that use GitHub actions for
+[playground](playground.md) projects.
+
+The Gradle Enterprise Server is running on AWS (`us-east-1`). This service is
+running on a Kubernetes cluster and can serve HTTP2 traffic. This service is
+managed by the Gradle team.
+
+The `FQHN` for the service is `hosted-ge-androidx.gradle.com` (port `443`).
+
+Note: The service will always use a `Location` header `ge.androidx.dev`. That is
+the only `hostname` it deems valid for a request origin.
diff --git a/docs/lint_guide.md b/docs/lint_guide.md
index f3557c3..c89e5d0 100644
--- a/docs/lint_guide.md
+++ b/docs/lint_guide.md
@@ -88,7 +88,7 @@
`CURRENT_API` is defined by the Lint API version against which your project is
compiled, as defined in the module's `build.gradle` file. Jetpack Lint modules
should compile using the Lint API version referenced in
-[Dependencies.kt](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt;l=176).
+[Dependencies.kt](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/public/src/main/kotlin/androidx/build/dependencies/Dependencies.kt;l=176).
We guarantee that our Lint checks work with the versions referenced by `minApi`
and `api` by running our tests with both versions. For newer versions of Android
diff --git a/docs/macrobenchmarking.md b/docs/macrobenchmarking.md
index a68fa46..69e6251 100644
--- a/docs/macrobenchmarking.md
+++ b/docs/macrobenchmarking.md
@@ -18,15 +18,15 @@
<td>In-process test of CPU work</td>
</tr>
<tr>
- <td>Slow iteration speed (Often several minutes)</td>
+ <td>Slow iteration speed (Often more than a minute)</td>
<td>Fast iteration speed (Often less than 10 seconds)</td>
</tr>
<tr>
<td>Results come with profiling traces</td>
- <td>Optional method sampling/tracing</td>
+ <td>Optional stack sampling/method tracing</td>
</tr>
<tr>
- <td>Min API 29</td>
+ <td>Min API 23</td>
<td>Min API 14</td>
</tr>
</table>
@@ -49,8 +49,6 @@
#### Kotlin {.new-tab}
```kotlin
-@RunWith(AndroidJUnit4::class)
-class MyStartupBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@@ -67,13 +65,31 @@
intent.setAction("mypackage.myapp.myaction")
startActivityAndWait(intent)
}
-}
```
#### Java {.new-tab}
```java
-// TODO: Java APIs are not yet available.
+ @Rule
+ public MacrobenchmarkRule mBenchmarkRule = MacrobenchmarkRule()
+
+ @Test
+ public void startup() {
+ mBenchmarkRule.measureRepeated(
+ "mypackage.myapp",
+ Collections.singletonList(new StartupTimingMetric()),
+ StartupMode.COLD,
+ /* iterations = */ 5,
+ scope -> {
+ scope.pressHome();
+ Intent intent = Intent();
+ intent.setPackage("mypackage.myapp");
+ intent.setAction("mypackage.myapp.myaction");
+ scope.startActivityAndWait(intent);
+ return Unit.INSTANCE;
+ }
+ );
+ }
```
</section>
diff --git a/docs/onboarding.md b/docs/onboarding.md
index 3af170e..005a845 100644
--- a/docs/onboarding.md
+++ b/docs/onboarding.md
@@ -618,7 +618,7 @@
topic) with the dependent library code.
Libraries typically reference dependencies using constants defined in
-[`Dependencies.kt`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt),
+[`Dependencies.kt`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/public/src/main/kotlin/androidx/build/dependencies/Dependencies.kt),
so please update this file to include a constant for the version of the library
that you have checked in. You will reference this constant in your library's
`build.gradle` dependencies.
@@ -888,3 +888,22 @@
Make sure the library versions are the same before and after replacement. Then
you can build the Android platform code with the new `androidx` code.
+
+### How do I measure library size? {#library-size}
+
+Method count and bytecode size are tracked in CI
+[alongside benchmarks](benchmarking.md#monitoring) to detect regressions.
+
+For local measurements, use the `:reportLibraryMetrics` task. For example:
+
+```shell
+./gradlew benchmark:benchmark-macro:reportLibraryMetrics
+cat ../../out/dist/librarymetrics/androidx.benchmark_benchmark-macro.json
+```
+
+Will output something like: `{"method_count":1256,"bytecode_size":178822}`
+
+Note: this only counts the weight of your library's jar/aar, including
+resources. It does not count library dependencies. It does not account for a
+minification step (e.g. with R8), as that is dynamic, and done at app build time
+(and depend on which entrypoints the app uses).
diff --git a/docs/testability.md b/docs/testability.md
index 52d60a9..ea5c3e3 100644
--- a/docs/testability.md
+++ b/docs/testability.md
@@ -41,7 +41,7 @@
To get started with sample code, see
[Sample code in Kotlin modules](api_guidelines.md#sample-code-in-kotlin-modules)
for information on writing samples that can be referenced from API reference
-documentation or [Project directory structure](policies.md#directory-structure)
+documentation or [Project directory structure](api_guidelines.md#module-structure)
for module naming guidelines if you'd like to create a basic test app.
### Avoiding side-effects {#side-effects}
diff --git a/docs/testing.md b/docs/testing.md
index 97ed2a9..d6ad752 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -35,7 +35,7 @@
### What gets tested, and when
We use the
-[AffectedModuleDetector](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt)
+[AffectedModuleDetector](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/private/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt)
to determine what projects have changed since the last merge.
In presubmit, "affected" modules will run all host and device tests regardless
@@ -153,6 +153,12 @@
do NOT open the AndroidX project in the "normal" instance of Android Studio;
instead, open a normal app or create a blank project using the app wizard.
+NOTE You can reuse the emulator and system images from a "normal" installation
+of Android Studio by linking the `emulator` and `system_images` directories to a
+standard Android SDK path and restarting Android Studio: `cd
+prebuilts/fullsdk-darwin ln -s ~/Library/Android/sdk/emulator emulator ln -s
+~/Library/Android/sdk/system-images system-images`
+
## Debugging with platform SDK sources {#sources}
The platform SDK sources that are checked into the development branch may not
diff --git a/docs/versioning.md b/docs/versioning.md
index 4c45a9b..88250d7 100644
--- a/docs/versioning.md
+++ b/docs/versioning.md
@@ -340,7 +340,7 @@
### How to update your version
1. Update the version listed in
- `frameworks/support/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt`
+ `frameworks/support/buildSrc/public/src/main/kotlin/androidx/build/LibraryVersions.kt`
1. If your library is a `beta` or `rc01` version, run `./gradlew
<your-lib>:updateApi`. This will create an API txt file for the new version
of your library. For other versions, this step is not reqired
diff --git a/enterprise/enterprise-feedback/build.gradle b/enterprise/enterprise-feedback/build.gradle
index df1412f..fd285db 100644
--- a/enterprise/enterprise-feedback/build.gradle
+++ b/enterprise/enterprise-feedback/build.gradle
@@ -23,11 +23,6 @@
testOptions.unitTests.includeAndroidResources = true
}
-// Robolectric requires more memory than Gradle default
-tasks.withType(Test) { t ->
- t.maxHeapSize = "1024m"
-}
-
androidx {
name = "Enterprise Feedback"
publish = Publish.SNAPSHOT_AND_RELEASE
diff --git a/fragment/fragment-ktx/build.gradle b/fragment/fragment-ktx/build.gradle
index f48c186..fdc614d 100644
--- a/fragment/fragment-ktx/build.gradle
+++ b/fragment/fragment-ktx/build.gradle
@@ -39,7 +39,7 @@
because 'Mirror fragment dependency graph for -ktx artifacts'
}
api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
- api("androidx.savedstate:savedstate-ktx:1.1.0") {
+ api(projectOrArtifact(":savedstate:savedstate-ktx")) {
because 'Mirror fragment dependency graph for -ktx artifacts'
}
api(libs.kotlinStdlib)
diff --git a/fragment/fragment/build.gradle b/fragment/fragment/build.gradle
index eedd7b9..e273c7d 100644
--- a/fragment/fragment/build.gradle
+++ b/fragment/fragment/build.gradle
@@ -33,7 +33,7 @@
api("androidx.lifecycle:lifecycle-livedata-core:2.3.1")
api("androidx.lifecycle:lifecycle-viewmodel:2.3.1")
api("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1")
- api("androidx.savedstate:savedstate:1.1.0")
+ api(projectOrArtifact(":savedstate:savedstate"))
api("androidx.annotation:annotation-experimental:1.0.0")
api(libs.kotlinStdlib)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentSavedStateRegistryTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentSavedStateRegistryTest.kt
index c544c120..bfffb85 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentSavedStateRegistryTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentSavedStateRegistryTest.kt
@@ -17,16 +17,22 @@
package androidx.fragment.app
import android.os.Bundle
+import androidx.activity.result.ActivityResultRegistry
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.app.ActivityOptionsCompat
+import androidx.fragment.app.test.ViewModelActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Lifecycle.State.CREATED
import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.ViewModelProvider
import androidx.savedstate.SavedStateRegistry
+import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.testutils.RecreatedActivity
-import androidx.testutils.recreate
+import androidx.testutils.withActivity
import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,52 +40,68 @@
@LargeTest
class FragmentSavedStateRegistryTest {
- @Suppress("DEPRECATION")
- @get:Rule
- var activityRule = androidx.test.rule.ActivityTestRule(FragmentSavedStateActivity::class.java)
-
- private fun initializeSavedState(testFragment: Fragment = Fragment()) {
- activityRule.runOnUiThread {
- val fragmentManager = activityRule.activity.supportFragmentManager
- fragmentManager.beginTransaction().add(testFragment, FRAGMENT_TAG).commitNow()
- assertThat(fragmentManager.findFragmentByTag(FRAGMENT_TAG)).isNotNull()
- assertThat(testFragment.lifecycle.currentState.isAtLeast(CREATED)).isTrue()
- val registry = testFragment.savedStateRegistry
- val savedState = registry.consumeRestoredStateForKey(CALLBACK_KEY)
- assertThat(savedState).isNull()
- registry.registerSavedStateProvider(CALLBACK_KEY, DefaultProvider())
- }
+ private fun ActivityScenario<FragmentSavedStateActivity>.initializeSavedState(
+ testFragment: Fragment = Fragment()
+ ) = withActivity {
+ val fragmentManager = supportFragmentManager
+ fragmentManager.beginTransaction().add(testFragment, FRAGMENT_TAG).commitNow()
+ assertThat(fragmentManager.findFragmentByTag(FRAGMENT_TAG)).isNotNull()
+ assertThat(testFragment.lifecycle.currentState.isAtLeast(CREATED)).isTrue()
+ val registry = testFragment.savedStateRegistry
+ val savedState = registry.consumeRestoredStateForKey(CALLBACK_KEY)
+ assertThat(savedState).isNull()
+ registry.registerSavedStateProvider(CALLBACK_KEY, DefaultProvider())
}
@Test
fun savedState() {
- initializeSavedState()
- val recreated = activityRule.recreate()
- activityRule.runOnUiThread {
- assertThat(recreated.fragment().lifecycle.currentState.isAtLeast(CREATED)).isTrue()
- checkDefaultSavedState(recreated.fragment().savedStateRegistry)
+ with(ActivityScenario.launch(FragmentSavedStateActivity::class.java)) {
+ initializeSavedState()
+ recreate()
+ withActivity {
+ assertThat(fragment.lifecycle.currentState.isAtLeast(CREATED)).isTrue()
+ checkDefaultSavedState(fragment.savedStateRegistry)
+ }
}
}
@Test
fun savedStateLateInit() {
- initializeSavedState()
- val recreated = activityRule.recreate()
- activityRule.runOnUiThread {
- recreated.fragment().lifecycle.addObserver(
- LifecycleEventObserver { _, event ->
- if (event == Lifecycle.Event.ON_RESUME) {
- checkDefaultSavedState(recreated.fragment().savedStateRegistry)
+ with(ActivityScenario.launch(FragmentSavedStateActivity::class.java)) {
+ initializeSavedState()
+ recreate()
+ withActivity {
+ fragment.lifecycle.addObserver(
+ LifecycleEventObserver { _, event ->
+ if (event == Lifecycle.Event.ON_RESUME) {
+ checkDefaultSavedState(fragment.savedStateRegistry)
+ }
}
- }
- )
+ )
+ }
}
}
@Test
fun savedStateEarlyRegister() {
- initializeSavedState(OnCreateCheckingFragment())
- activityRule.recreate()
+ with(ActivityScenario.launch(FragmentSavedStateActivity::class.java)) {
+ initializeSavedState(OnCreateCheckingFragment())
+ recreate()
+ }
+ }
+
+ @Test
+ fun savedStateOnActivityResult() {
+ with(ActivityScenario.launch(FragmentSavedStateActivity::class.java)) {
+ val registry = withActivity { registry }
+ initializeSavedState(OnActivityResultCheckingFragment(registry))
+ recreate()
+ moveToState(Lifecycle.State.CREATED)
+ withActivity {
+ (fragment as OnActivityResultCheckingFragment).launcher.launch("")
+ }
+ moveToState(Lifecycle.State.RESUMED)
+ }
}
}
@@ -90,7 +112,32 @@
}
class FragmentSavedStateActivity : RecreatedActivity() {
- fun fragment() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG)
+ val registry = object : ActivityResultRegistry() {
+ override fun <I : Any?, O : Any?> onLaunch(
+ requestCode: Int,
+ contract: ActivityResultContract<I, O>,
+ input: I,
+ options: ActivityOptionsCompat?
+ ) {
+ if (contract is ActivityResultContracts.GetContent) {
+ dispatchResult(requestCode, null)
+ }
+ }
+ }
+
+ init {
+ supportFragmentManager.fragmentFactory = object : FragmentFactory() {
+ override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
+ return when (loadFragmentClass(classLoader, className)) {
+ OnActivityResultCheckingFragment::class.java ->
+ OnActivityResultCheckingFragment(registry)
+ else -> super.instantiate(classLoader, className)
+ }
+ }
+ }
+ }
+
+ val fragment: Fragment get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG)
?: throw IllegalStateException("Fragment under test wasn't found")
}
@@ -103,6 +150,23 @@
}
}
+class OnActivityResultCheckingFragment(
+ activityResultRegistry: ActivityResultRegistry
+) : Fragment() {
+ val viewModel by lazy {
+ ViewModelProvider(this).get(ViewModelActivity.TestSavedStateViewModel::class.java)
+ }
+
+ val launcher = registerForActivityResult(
+ ActivityResultContracts.GetContent(),
+ activityResultRegistry
+ ) {
+ // Accessing the ViewModel is what triggers the startup behavior of
+ // SavedStateRegistryController
+ viewModel
+ }
+}
+
private class DefaultProvider : SavedStateRegistry.SavedStateProvider {
override fun saveState() = Bundle().apply { putString(KEY, VALUE) }
}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
index 8dc962e..8fcec63 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -572,6 +572,12 @@
// The default factory depends on the SavedStateRegistry so it
// needs to be reset when the SavedStateRegistry is reset
mDefaultFactory = null;
+ registerOnPreAttachListener(new OnPreAttachedListener() {
+ @Override
+ void onPreAttached() {
+ mSavedStateRegistryController.performAttach();
+ }
+ });
}
/**
diff --git a/glance/glance-appwidget-proto/build.gradle b/glance/glance-appwidget-proto/build.gradle
index 0e76d43..0346460 100644
--- a/glance/glance-appwidget-proto/build.gradle
+++ b/glance/glance-appwidget-proto/build.gradle
@@ -37,7 +37,7 @@
dependencies {
api(libs.kotlinStdlib)
- implementation project(path: ':datastore:datastore-core')
+ implementation("androidx.datastore:datastore-core:1.0.0")
shadowed(libs.protobufLite)
}
@@ -97,7 +97,6 @@
androidx {
name = "Glance AppWidget Protos"
type = LibraryType.PUBLISHED_LIBRARY
- publish = Publish.SNAPSHOT_ONLY
mavenGroup = LibraryGroups.GLANCE
inceptionYear = "2021"
runApiTasks = new RunApiTasks.No("No public API required for internal use with app widgets.")
diff --git a/glance/glance-appwidget/api/current.txt b/glance/glance-appwidget/api/current.txt
index b3a2824..4f93a5f 100644
--- a/glance/glance-appwidget/api/current.txt
+++ b/glance/glance-appwidget/api/current.txt
@@ -27,7 +27,7 @@
}
public final class CheckBoxKt {
- method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors);
+ method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors);
method public static androidx.glance.appwidget.CheckBoxColors CheckBoxColors(long checkedColor, long uncheckedColor);
method public static androidx.glance.appwidget.CheckBoxColors CheckBoxColors(optional androidx.glance.unit.ColorProvider? checkedColor, optional androidx.glance.unit.ColorProvider? uncheckedColor);
method public static androidx.glance.appwidget.CheckBoxColors CheckBoxColors(@ColorRes int checkBoxColor);
@@ -35,9 +35,7 @@
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> getLocalAppWidgetOptions();
- method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.String> getLocalUiKey();
property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> LocalAppWidgetOptions;
- property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.String> LocalUiKey;
}
public final class CornerRadiusKt {
@@ -56,12 +54,15 @@
method @androidx.compose.runtime.Composable public abstract void Content();
method public androidx.glance.appwidget.SizeMode getSizeMode();
method public androidx.glance.state.GlanceStateDefinition<?>? getStateDefinition();
+ method public suspend Object? onDelete(androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
method public final suspend Object? update(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
property public androidx.glance.appwidget.SizeMode sizeMode;
property public androidx.glance.state.GlanceStateDefinition<?>? stateDefinition;
}
public final class GlanceAppWidgetKt {
+ method public static suspend Object? updateAll(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public static suspend inline <reified State> void updateIf(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.jvm.functions.Function1<? super State,? extends java.lang.Boolean> predicate);
}
public final class GlanceAppWidgetManager {
@@ -89,6 +90,9 @@
public final class LayoutSelectionKt {
}
+ public final class ListAdapterTrampolineKt {
+ }
+
public final class NormalizeCompositionTreeKt {
}
@@ -122,7 +126,7 @@
}
public final class SwitchKt {
- method @androidx.compose.runtime.Composable public static void Switch(boolean checked, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors);
+ method @androidx.compose.runtime.Composable public static void Switch(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors);
method public static androidx.glance.appwidget.SwitchColors SwitchColors(optional androidx.glance.unit.ColorProvider? checkedThumbColor, optional androidx.glance.unit.ColorProvider? uncheckedThumbColor, optional androidx.glance.unit.ColorProvider? checkedTrackColor, optional androidx.glance.unit.ColorProvider? uncheckedTrackColor);
method public static androidx.glance.appwidget.SwitchColors SwitchColors(@ColorRes int thumbColor, optional @ColorRes int trackColor);
}
@@ -142,7 +146,22 @@
}
public final class LaunchActivityIntentActionKt {
- method public static androidx.glance.action.Action actionLaunchActivity(android.content.Intent intent);
+ method public static androidx.glance.action.Action actionLaunchActivity(android.content.Intent intent, optional androidx.glance.action.ActionParameters parameters);
+ }
+
+ public final class LaunchBroadcastActionKt {
+ method public static androidx.glance.action.Action actionLaunchBroadcastReceiver(String action, optional android.content.ComponentName? componentName);
+ method public static androidx.glance.action.Action actionLaunchBroadcastReceiver(android.content.Intent intent);
+ method public static androidx.glance.action.Action actionLaunchBroadcastReceiver(android.content.ComponentName componentName);
+ method public static <T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionLaunchBroadcastReceiver(Class<T> receiver);
+ method public static inline <reified T extends android.content.BroadcastReceiver> androidx.glance.action.Action! actionLaunchBroadcastReceiver();
+ }
+
+ public final class LaunchServiceActionKt {
+ method public static androidx.glance.action.Action actionLaunchService(android.content.Intent intent, optional boolean isForegroundService);
+ method public static androidx.glance.action.Action actionLaunchService(android.content.ComponentName componentName, optional boolean isForegroundService);
+ method public static <T extends android.app.Service> androidx.glance.action.Action actionLaunchService(Class<T> service, optional boolean isForegroundService);
+ method public static inline <reified T extends android.app.Service> androidx.glance.action.Action! actionLaunchService(optional boolean isForegroundService);
}
public final class RunCallbackActionKt {
@@ -150,6 +169,11 @@
method public static inline <reified T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action! actionRunCallback(optional androidx.glance.action.ActionParameters parameters);
}
+ public final class ToggleableKt {
+ method public static androidx.glance.action.ActionParameters.Key<java.lang.Boolean> getToggleableStateKey();
+ property public static final androidx.glance.action.ActionParameters.Key<java.lang.Boolean> ToggleableStateKey;
+ }
+
}
package androidx.glance.appwidget.lazy {
@@ -181,6 +205,17 @@
}
+package androidx.glance.appwidget.state {
+
+ public final class GlanceAppWidgetStateKt {
+ method public static suspend <T> Object? getAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T> p);
+ method public static suspend <T> Object? getAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T> p);
+ method public static suspend <T> Object? updateAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T> p);
+ method public static suspend <T> Object? updateAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T> p);
+ }
+
+}
+
package androidx.glance.appwidget.translators {
public final class CheckBoxTranslatorKt {
diff --git a/glance/glance-appwidget/api/public_plus_experimental_current.txt b/glance/glance-appwidget/api/public_plus_experimental_current.txt
index b3a2824..4f93a5f 100644
--- a/glance/glance-appwidget/api/public_plus_experimental_current.txt
+++ b/glance/glance-appwidget/api/public_plus_experimental_current.txt
@@ -27,7 +27,7 @@
}
public final class CheckBoxKt {
- method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors);
+ method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors);
method public static androidx.glance.appwidget.CheckBoxColors CheckBoxColors(long checkedColor, long uncheckedColor);
method public static androidx.glance.appwidget.CheckBoxColors CheckBoxColors(optional androidx.glance.unit.ColorProvider? checkedColor, optional androidx.glance.unit.ColorProvider? uncheckedColor);
method public static androidx.glance.appwidget.CheckBoxColors CheckBoxColors(@ColorRes int checkBoxColor);
@@ -35,9 +35,7 @@
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> getLocalAppWidgetOptions();
- method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.String> getLocalUiKey();
property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> LocalAppWidgetOptions;
- property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.String> LocalUiKey;
}
public final class CornerRadiusKt {
@@ -56,12 +54,15 @@
method @androidx.compose.runtime.Composable public abstract void Content();
method public androidx.glance.appwidget.SizeMode getSizeMode();
method public androidx.glance.state.GlanceStateDefinition<?>? getStateDefinition();
+ method public suspend Object? onDelete(androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
method public final suspend Object? update(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
property public androidx.glance.appwidget.SizeMode sizeMode;
property public androidx.glance.state.GlanceStateDefinition<?>? stateDefinition;
}
public final class GlanceAppWidgetKt {
+ method public static suspend Object? updateAll(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public static suspend inline <reified State> void updateIf(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.jvm.functions.Function1<? super State,? extends java.lang.Boolean> predicate);
}
public final class GlanceAppWidgetManager {
@@ -89,6 +90,9 @@
public final class LayoutSelectionKt {
}
+ public final class ListAdapterTrampolineKt {
+ }
+
public final class NormalizeCompositionTreeKt {
}
@@ -122,7 +126,7 @@
}
public final class SwitchKt {
- method @androidx.compose.runtime.Composable public static void Switch(boolean checked, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors);
+ method @androidx.compose.runtime.Composable public static void Switch(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors);
method public static androidx.glance.appwidget.SwitchColors SwitchColors(optional androidx.glance.unit.ColorProvider? checkedThumbColor, optional androidx.glance.unit.ColorProvider? uncheckedThumbColor, optional androidx.glance.unit.ColorProvider? checkedTrackColor, optional androidx.glance.unit.ColorProvider? uncheckedTrackColor);
method public static androidx.glance.appwidget.SwitchColors SwitchColors(@ColorRes int thumbColor, optional @ColorRes int trackColor);
}
@@ -142,7 +146,22 @@
}
public final class LaunchActivityIntentActionKt {
- method public static androidx.glance.action.Action actionLaunchActivity(android.content.Intent intent);
+ method public static androidx.glance.action.Action actionLaunchActivity(android.content.Intent intent, optional androidx.glance.action.ActionParameters parameters);
+ }
+
+ public final class LaunchBroadcastActionKt {
+ method public static androidx.glance.action.Action actionLaunchBroadcastReceiver(String action, optional android.content.ComponentName? componentName);
+ method public static androidx.glance.action.Action actionLaunchBroadcastReceiver(android.content.Intent intent);
+ method public static androidx.glance.action.Action actionLaunchBroadcastReceiver(android.content.ComponentName componentName);
+ method public static <T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionLaunchBroadcastReceiver(Class<T> receiver);
+ method public static inline <reified T extends android.content.BroadcastReceiver> androidx.glance.action.Action! actionLaunchBroadcastReceiver();
+ }
+
+ public final class LaunchServiceActionKt {
+ method public static androidx.glance.action.Action actionLaunchService(android.content.Intent intent, optional boolean isForegroundService);
+ method public static androidx.glance.action.Action actionLaunchService(android.content.ComponentName componentName, optional boolean isForegroundService);
+ method public static <T extends android.app.Service> androidx.glance.action.Action actionLaunchService(Class<T> service, optional boolean isForegroundService);
+ method public static inline <reified T extends android.app.Service> androidx.glance.action.Action! actionLaunchService(optional boolean isForegroundService);
}
public final class RunCallbackActionKt {
@@ -150,6 +169,11 @@
method public static inline <reified T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action! actionRunCallback(optional androidx.glance.action.ActionParameters parameters);
}
+ public final class ToggleableKt {
+ method public static androidx.glance.action.ActionParameters.Key<java.lang.Boolean> getToggleableStateKey();
+ property public static final androidx.glance.action.ActionParameters.Key<java.lang.Boolean> ToggleableStateKey;
+ }
+
}
package androidx.glance.appwidget.lazy {
@@ -181,6 +205,17 @@
}
+package androidx.glance.appwidget.state {
+
+ public final class GlanceAppWidgetStateKt {
+ method public static suspend <T> Object? getAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T> p);
+ method public static suspend <T> Object? getAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T> p);
+ method public static suspend <T> Object? updateAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T> p);
+ method public static suspend <T> Object? updateAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T> p);
+ }
+
+}
+
package androidx.glance.appwidget.translators {
public final class CheckBoxTranslatorKt {
diff --git a/glance/glance-appwidget/api/restricted_current.txt b/glance/glance-appwidget/api/restricted_current.txt
index b3a2824..4f93a5f 100644
--- a/glance/glance-appwidget/api/restricted_current.txt
+++ b/glance/glance-appwidget/api/restricted_current.txt
@@ -27,7 +27,7 @@
}
public final class CheckBoxKt {
- method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors);
+ method @androidx.compose.runtime.Composable public static void CheckBox(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.CheckBoxColors colors);
method public static androidx.glance.appwidget.CheckBoxColors CheckBoxColors(long checkedColor, long uncheckedColor);
method public static androidx.glance.appwidget.CheckBoxColors CheckBoxColors(optional androidx.glance.unit.ColorProvider? checkedColor, optional androidx.glance.unit.ColorProvider? uncheckedColor);
method public static androidx.glance.appwidget.CheckBoxColors CheckBoxColors(@ColorRes int checkBoxColor);
@@ -35,9 +35,7 @@
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> getLocalAppWidgetOptions();
- method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.String> getLocalUiKey();
property public static final androidx.compose.runtime.ProvidableCompositionLocal<android.os.Bundle> LocalAppWidgetOptions;
- property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.String> LocalUiKey;
}
public final class CornerRadiusKt {
@@ -56,12 +54,15 @@
method @androidx.compose.runtime.Composable public abstract void Content();
method public androidx.glance.appwidget.SizeMode getSizeMode();
method public androidx.glance.state.GlanceStateDefinition<?>? getStateDefinition();
+ method public suspend Object? onDelete(androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
method public final suspend Object? update(android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
property public androidx.glance.appwidget.SizeMode sizeMode;
property public androidx.glance.state.GlanceStateDefinition<?>? stateDefinition;
}
public final class GlanceAppWidgetKt {
+ method public static suspend Object? updateAll(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public static suspend inline <reified State> void updateIf(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, kotlin.jvm.functions.Function1<? super State,? extends java.lang.Boolean> predicate);
}
public final class GlanceAppWidgetManager {
@@ -89,6 +90,9 @@
public final class LayoutSelectionKt {
}
+ public final class ListAdapterTrampolineKt {
+ }
+
public final class NormalizeCompositionTreeKt {
}
@@ -122,7 +126,7 @@
}
public final class SwitchKt {
- method @androidx.compose.runtime.Composable public static void Switch(boolean checked, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors);
+ method @androidx.compose.runtime.Composable public static void Switch(boolean checked, androidx.glance.action.Action? onCheckedChange, optional androidx.glance.GlanceModifier modifier, optional String text, optional androidx.glance.text.TextStyle? style, optional androidx.glance.appwidget.SwitchColors colors);
method public static androidx.glance.appwidget.SwitchColors SwitchColors(optional androidx.glance.unit.ColorProvider? checkedThumbColor, optional androidx.glance.unit.ColorProvider? uncheckedThumbColor, optional androidx.glance.unit.ColorProvider? checkedTrackColor, optional androidx.glance.unit.ColorProvider? uncheckedTrackColor);
method public static androidx.glance.appwidget.SwitchColors SwitchColors(@ColorRes int thumbColor, optional @ColorRes int trackColor);
}
@@ -142,7 +146,22 @@
}
public final class LaunchActivityIntentActionKt {
- method public static androidx.glance.action.Action actionLaunchActivity(android.content.Intent intent);
+ method public static androidx.glance.action.Action actionLaunchActivity(android.content.Intent intent, optional androidx.glance.action.ActionParameters parameters);
+ }
+
+ public final class LaunchBroadcastActionKt {
+ method public static androidx.glance.action.Action actionLaunchBroadcastReceiver(String action, optional android.content.ComponentName? componentName);
+ method public static androidx.glance.action.Action actionLaunchBroadcastReceiver(android.content.Intent intent);
+ method public static androidx.glance.action.Action actionLaunchBroadcastReceiver(android.content.ComponentName componentName);
+ method public static <T extends android.content.BroadcastReceiver> androidx.glance.action.Action actionLaunchBroadcastReceiver(Class<T> receiver);
+ method public static inline <reified T extends android.content.BroadcastReceiver> androidx.glance.action.Action! actionLaunchBroadcastReceiver();
+ }
+
+ public final class LaunchServiceActionKt {
+ method public static androidx.glance.action.Action actionLaunchService(android.content.Intent intent, optional boolean isForegroundService);
+ method public static androidx.glance.action.Action actionLaunchService(android.content.ComponentName componentName, optional boolean isForegroundService);
+ method public static <T extends android.app.Service> androidx.glance.action.Action actionLaunchService(Class<T> service, optional boolean isForegroundService);
+ method public static inline <reified T extends android.app.Service> androidx.glance.action.Action! actionLaunchService(optional boolean isForegroundService);
}
public final class RunCallbackActionKt {
@@ -150,6 +169,11 @@
method public static inline <reified T extends androidx.glance.appwidget.action.ActionCallback> androidx.glance.action.Action! actionRunCallback(optional androidx.glance.action.ActionParameters parameters);
}
+ public final class ToggleableKt {
+ method public static androidx.glance.action.ActionParameters.Key<java.lang.Boolean> getToggleableStateKey();
+ property public static final androidx.glance.action.ActionParameters.Key<java.lang.Boolean> ToggleableStateKey;
+ }
+
}
package androidx.glance.appwidget.lazy {
@@ -181,6 +205,17 @@
}
+package androidx.glance.appwidget.state {
+
+ public final class GlanceAppWidgetStateKt {
+ method public static suspend <T> Object? getAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T> p);
+ method public static suspend <T> Object? getAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.coroutines.Continuation<? super T> p);
+ method public static suspend <T> Object? updateAppWidgetState(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T> p);
+ method public static suspend <T> Object? updateAppWidgetState(androidx.glance.appwidget.GlanceAppWidget, android.content.Context context, androidx.glance.GlanceId glanceId, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateState, kotlin.coroutines.Continuation<? super T> p);
+ }
+
+}
+
package androidx.glance.appwidget.translators {
public final class CheckBoxTranslatorKt {
diff --git a/glance/glance-appwidget/build.gradle b/glance/glance-appwidget/build.gradle
index 79caab8..0567a06 100644
--- a/glance/glance-appwidget/build.gradle
+++ b/glance/glance-appwidget/build.gradle
@@ -40,16 +40,15 @@
api("androidx.compose.ui:ui-graphics:1.1.0-beta01")
api("androidx.compose.ui:ui-unit:1.1.0-beta01")
- implementation project(path: ':core:core-remoteviews')
- implementation project(path: ':core:core-ktx')
- implementation project(path: ':datastore:datastore-core')
- implementation project(path: ':datastore:datastore')
+ implementation('androidx.core:core-ktx:1.7.0')
+ implementation("androidx.datastore:datastore:1.0.0")
+ implementation("androidx.datastore:datastore-core:1.0.0")
implementation("androidx.datastore:datastore-preferences-core:1.0.0")
implementation("androidx.datastore:datastore-preferences:1.0.0")
+ implementation(project(':core:core-remoteviews'))
implementation(project(":glance:glance-appwidget-proto"))
implementation(libs.kotlinStdlib)
- testImplementation(project(":core:core-ktx"))
testImplementation(libs.junit)
testImplementation(libs.kotlinCoroutinesTest)
testImplementation(libs.kotlinTest)
@@ -61,13 +60,14 @@
testImplementation(libs.testRunner)
testImplementation(libs.truth)
testImplementation(libs.kotlinReflect)
+ testImplementation('androidx.core:core-ktx:1.7.0')
testImplementation("androidx.datastore:datastore-core:1.0.0")
testImplementation("androidx.datastore:datastore-preferences-core:1.0.0")
testImplementation("androidx.datastore:datastore-preferences:1.0.0")
- androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
androidTestImplementation(project(":test:screenshot:screenshot"))
- androidTestImplementation(project(":core:core-ktx"))
+ androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
+ androidTestImplementation('androidx.core:core-ktx:1.7.0')
androidTestImplementation(libs.espressoCore)
androidTestImplementation(libs.espressoIdlingResource)
androidTestImplementation(libs.kotlinCoroutinesTest)
@@ -99,7 +99,6 @@
androidx {
name = "Glance For App Widgets"
type = LibraryType.PUBLISHED_LIBRARY
- publish = Publish.SNAPSHOT_ONLY
mavenGroup = LibraryGroups.GLANCE
inceptionYear = "2021"
description = "Glance-appwidgets allows developers to build layouts for Android AppWidgets " +
diff --git a/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/LayoutGenerator.kt b/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/LayoutGenerator.kt
index 6a82308..74ce99f 100644
--- a/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/LayoutGenerator.kt
+++ b/glance/glance-appwidget/glance-layout-generator/src/main/kotlin/androidx/glance/appwidget/layoutgenerator/LayoutGenerator.kt
@@ -323,6 +323,7 @@
setNamedItemNS(androidId("@id/rootView"))
setNamedItemNS(androidWidth(width))
setNamedItemNS(androidHeight(height))
+ setNamedItemNS(androidAttr("theme", "@style/Glance.AppWidget.Theme"))
}
val stub = createElement("ViewStub")
root.appendChild(stub)
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml b/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml
index 0d11ac0..6039b6b 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/AndroidManifest.xml
@@ -34,6 +34,7 @@
</activity>
<activity android:name=".ActionDemoActivity" />
+ <service android:name=".ActionDemoService" />
<activity android:name=".ListClickDestinationActivity" android:exported="false" />
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt
index fd7fcd2..c7e91e5 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ActionAppWidget.kt
@@ -16,43 +16,61 @@
package androidx.glance.appwidget.demos
-import android.app.Activity
+import android.app.Service
+import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.os.IBinder
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.intPreferencesKey
import androidx.glance.Button
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
-import androidx.glance.Image
-import androidx.glance.ImageProvider
import androidx.glance.LocalContext
-import androidx.glance.appwidget.action.ActionCallback
import androidx.glance.action.ActionParameters
import androidx.glance.action.actionLaunchActivity
import androidx.glance.action.actionParametersOf
-import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.action.clickable
+import androidx.glance.action.toParametersKey
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
+import androidx.glance.appwidget.action.ActionCallback
import androidx.glance.appwidget.action.actionLaunchActivity
+import androidx.glance.appwidget.action.actionLaunchBroadcastReceiver
+import androidx.glance.appwidget.action.actionLaunchService
+import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.appwidget.appWidgetBackground
import androidx.glance.appwidget.cornerRadius
+import androidx.glance.appwidget.state.updateAppWidgetState
+import androidx.glance.appwidget.unit.ColorProvider
+import androidx.glance.currentState
import androidx.glance.layout.Alignment
import androidx.glance.layout.Column
+import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxSize
+import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
-import androidx.glance.layout.size
+import androidx.glance.state.PreferencesGlanceStateDefinition
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextDecoration
import androidx.glance.text.TextStyle
-import java.util.concurrent.atomic.AtomicBoolean
class ActionAppWidget : GlanceAppWidget() {
+ override val stateDefinition = PreferencesGlanceStateDefinition
+
@Composable
override fun Content() {
Column(
@@ -61,71 +79,185 @@
verticalAlignment = Alignment.Vertical.CenterVertically,
horizontalAlignment = Alignment.Horizontal.CenterHorizontally
) {
- Text(
- text = "Tap to change me",
- style = TextStyle(
- fontSize = 16.sp, fontWeight = FontWeight.Bold,
- textDecoration = if (shouldChangeView.get()) {
- TextDecoration.Underline
- } else {
- TextDecoration.None
- }
- ),
- modifier = GlanceModifier
- .padding(8.dp)
- .clickable(
- actionRunCallback<UpdateAction>(
- actionParametersOf(
- KEY_USE_UNDERLINE to shouldChangeView.get()
- )
- )
- )
- )
- if (shouldChangeView.get()) {
- Image(
- ImageProvider(R.drawable.compose),
- "Compose",
- modifier = GlanceModifier.size(40.dp)
- )
+ Row(GlanceModifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
+ SelectableActionItem(label = "Activities", index = 0)
+ SelectableActionItem(label = "Services", index = 1)
+ SelectableActionItem(label = "Broadcasts", index = 2)
}
- Button(
- text = "Intent",
- onClick = actionLaunchActivity(
- Intent(LocalContext.current, ActionDemoActivity::class.java)
- )
- )
- Button(
- text = "Target class",
- onClick = actionLaunchActivity<ActionDemoActivity>()
- )
- Button(
- text = "Component name",
- onClick = actionLaunchActivity(
- ComponentName(LocalContext.current, ActionDemoActivity::class.java)
- )
- )
+
+ when (currentState<Preferences>()[selectedItemKey] ?: 0) {
+ 0 -> LaunchActivityActions()
+ 1 -> LaunchServiceActions()
+ 2 -> LaunchBroadcastReceiverActions()
+ else -> throw IllegalArgumentException("Wrong index selected")
+ }
}
}
}
-private val KEY_USE_UNDERLINE = ActionParameters.Key<Boolean>("underline")
-private var shouldChangeView = AtomicBoolean(false)
+private val selectedItemKey = intPreferencesKey("selectedItemKey")
+private val launchMessageKey = ActionParameters.Key<String>("launchMessageKey")
+
+@Composable
+private fun SelectableActionItem(label: String, index: Int) {
+ val style = if (index == currentState<Preferences>()[selectedItemKey] ?: 0) {
+ TextStyle(
+ fontSize = 16.sp,
+ fontWeight = FontWeight.Bold,
+ textDecoration = TextDecoration.Underline,
+ )
+ } else {
+ TextStyle(
+ fontSize = 16.sp,
+ fontWeight = FontWeight.Normal,
+ textDecoration = TextDecoration.None,
+ color = ColorProvider(
+ Color.Black.copy(alpha = 0.3f),
+ Color.White.copy(alpha = 0.3f)
+ )
+ )
+ }
+ Text(
+ text = label,
+ style = style,
+ modifier = GlanceModifier
+ .padding(8.dp)
+ .clickable(
+ actionRunCallback<UpdateAction>(
+ actionParametersOf(
+ selectedItemKey.toParametersKey() to index
+ )
+ )
+ )
+ )
+}
+
+@Composable
+private fun LaunchActivityActions() {
+ Button(
+ text = "Intent",
+ onClick = actionLaunchActivity(
+ Intent(LocalContext.current, ActionDemoActivity::class.java)
+ )
+ )
+ Button(
+ text = "Target class",
+ onClick = actionLaunchActivity<ActionDemoActivity>(
+ actionParametersOf(
+ launchMessageKey to "Launch activity by target class"
+ )
+ )
+ )
+ Button(
+ text = "Component name",
+ onClick = actionLaunchActivity(
+ ComponentName(LocalContext.current, ActionDemoActivity::class.java)
+ )
+ )
+}
+
+@Composable
+private fun LaunchServiceActions() {
+ Button(
+ text = "Intent",
+ onClick = actionLaunchService(
+ Intent(LocalContext.current, ActionDemoService::class.java)
+ )
+ )
+ Button(
+ text = "Target class",
+ onClick = actionLaunchService<ActionDemoService>()
+ )
+ Button(
+ text = "In foreground",
+ onClick = actionLaunchService<ActionDemoService>(isForegroundService = true)
+ )
+ Button(
+ text = "Component name",
+ onClick = actionLaunchService(
+ ComponentName(LocalContext.current, ActionDemoService::class.java)
+ )
+ )
+}
+
+@Composable
+private fun LaunchBroadcastReceiverActions() {
+ Button(
+ text = "Intent",
+ onClick = actionLaunchBroadcastReceiver(
+ Intent(LocalContext.current, ActionAppWidgetReceiver::class.java)
+ )
+ )
+ Button(
+ text = "Action",
+ onClick = actionLaunchBroadcastReceiver(
+ AppWidgetManager.ACTION_APPWIDGET_UPDATE
+ )
+ )
+ Button(
+ text = "Target class",
+ onClick = actionLaunchBroadcastReceiver<ActionAppWidgetReceiver>()
+ )
+ Button(
+ text = "Component name",
+ onClick = actionLaunchBroadcastReceiver(
+ ComponentName(LocalContext.current, ActionAppWidgetReceiver::class.java)
+ )
+ )
+}
/**
- * Action to update the [shouldChangeView] value whenever users clicks on text
+ * Action to update the [selectedItemKey] value whenever users clicks on text
*/
class UpdateAction : ActionCallback {
override suspend fun onRun(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
- shouldChangeView.set(!parameters.getOrDefault(KEY_USE_UNDERLINE, false))
- ActionAppWidget().update(context, glanceId)
+ val actionAppWidget = ActionAppWidget()
+ actionAppWidget.updateAppWidgetState<Preferences>(context, glanceId) { prefs ->
+ prefs.toMutablePreferences().apply {
+ this[selectedItemKey] = parameters[selectedItemKey.toParametersKey()] ?: 0
+ }
+ }
+ actionAppWidget.update(context, glanceId)
}
}
/**
* Placeholder activity to launch via [actionLaunchActivity]
*/
-class ActionDemoActivity : Activity()
+class ActionDemoActivity : ComponentActivity() {
+
+ override fun onResume() {
+ super.onResume()
+ setContent {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = androidx.compose.ui.Alignment.Center
+ ) {
+ val message = intent.getStringExtra(launchMessageKey.name) ?: "Not found"
+ androidx.compose.material.Text(message)
+ }
+ }
+ Log.d(this::class.simpleName, "Action Demo Activity: ${intent.extras}")
+ }
+}
+
+/**
+ * Placeholder service to launch via [actionLaunchService]
+ */
+class ActionDemoService : Service() {
+ override fun onBind(intent: Intent?): IBinder? = null
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ Log.d(this::class.simpleName, "Action Demo Service: $intent")
+ return super.onStartCommand(intent, flags, startId)
+ }
+}
class ActionAppWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = ActionAppWidget()
+
+ override fun onReceive(context: Context, intent: Intent) {
+ super.onReceive(context, intent)
+ Log.d(this::class.simpleName, "Action Demo Broadcast: $intent")
+ }
}
\ No newline at end of file
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/CompoundButtonAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/CompoundButtonAppWidget.kt
index fdb499c..8e83b33 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/CompoundButtonAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/CompoundButtonAppWidget.kt
@@ -19,32 +19,28 @@
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import androidx.datastore.core.DataStore
-import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
-import androidx.datastore.preferences.preferencesDataStoreFile
import androidx.glance.Button
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
-import androidx.glance.LocalSize
import androidx.glance.action.ActionParameters
-import androidx.glance.appwidget.action.ActionCallback
-import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.action.actionParametersOf
import androidx.glance.appwidget.CheckBox
import androidx.glance.appwidget.CheckBoxColors
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
-import androidx.glance.appwidget.LocalUiKey
import androidx.glance.appwidget.SizeMode
import androidx.glance.appwidget.Switch
import androidx.glance.appwidget.SwitchColors
+import androidx.glance.appwidget.action.ActionCallback
+import androidx.glance.appwidget.action.actionRunCallback
+import androidx.glance.appwidget.action.ToggleableStateKey
import androidx.glance.appwidget.appWidgetBackground
import androidx.glance.appwidget.cornerRadius
+import androidx.glance.appwidget.state.updateAppWidgetState
import androidx.glance.appwidget.unit.ColorProvider
import androidx.glance.background
import androidx.glance.currentState
@@ -54,23 +50,25 @@
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
-import androidx.glance.state.GlanceState
import androidx.glance.state.GlanceStateDefinition
+import androidx.glance.state.PreferencesGlanceStateDefinition
import androidx.glance.text.FontStyle
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
-import java.io.File
class CompoundButtonAppWidget : GlanceAppWidget() {
override val sizeMode: SizeMode = SizeMode.Exact
- override var stateDefinition: GlanceStateDefinition<*> = MyStateDefinition
+ override var stateDefinition: GlanceStateDefinition<*> = PreferencesGlanceStateDefinition
+
+ enum class Buttons {
+ CHECK_1, CHECK_2, CHECK_3, SWITCH_1, SWITCH_2;
+ val prefsKey = booleanPreferencesKey(name)
+ }
@Composable
override fun Content() {
- val size = LocalSize.current
- val toggled = size.width >= 150.dp
Column(
modifier = GlanceModifier.fillMaxSize().background(Color.LightGray)
.padding(R.dimen.external_padding).cornerRadius(R.dimen.corner_radius)
@@ -86,9 +84,25 @@
)
val fillModifier = GlanceModifier.fillMaxWidth()
- CheckBox(checked = toggled, text = "Checkbox 1")
+ val prefs = currentState<Preferences>()
+ val checkbox1Checked = prefs[Buttons.CHECK_1.prefsKey] ?: false
+ val checkbox2Checked = prefs[Buttons.CHECK_2.prefsKey] ?: false
+ val checkbox3Checked = prefs[Buttons.CHECK_3.prefsKey] ?: false
+ val switch1Checked = prefs[Buttons.SWITCH_1.prefsKey] ?: false
+ val switch2Checked = prefs[Buttons.SWITCH_2.prefsKey] ?: false
+
CheckBox(
- checked = !toggled,
+ checked = checkbox1Checked,
+ onCheckedChange = actionRunCallback<ToggleAction>(
+ actionParametersOf(eventTargetKey to Buttons.CHECK_1.name)
+ ),
+ text = "Checkbox 1"
+ )
+ CheckBox(
+ checked = checkbox2Checked,
+ onCheckedChange = actionRunCallback<ToggleAction>(
+ actionParametersOf(eventTargetKey to Buttons.CHECK_2.name)
+ ),
text = "Checkbox 2",
style = textStyle,
modifier = fillModifier,
@@ -98,22 +112,31 @@
)
)
CheckBox(
- checked = toggled,
+ checked = checkbox3Checked,
+ onCheckedChange = actionRunCallback<ToggleAction>(
+ actionParametersOf(eventTargetKey to Buttons.CHECK_3.name)
+ ),
text = "Checkbox 3",
colors = CheckBoxColors(R.color.my_checkbox_colors)
)
Switch(
- checked = toggled,
+ checked = switch1Checked,
+ onCheckedChange = actionRunCallback<ToggleAction>(
+ actionParametersOf(eventTargetKey to Buttons.SWITCH_1.name)
+ ),
text = "Switch 1",
colors = SwitchColors(
checkedThumbColor = ColorProvider(day = Color.Red, night = Color.Cyan),
uncheckedThumbColor = ColorProvider(day = Color.Green, night = Color.Magenta),
checkedTrackColor = ColorProvider(day = Color.Blue, night = Color.Yellow),
uncheckedTrackColor = ColorProvider(day = Color.Magenta, night = Color.Green)
- )
+ ),
)
Switch(
- checked = !toggled,
+ checked = switch2Checked,
+ onCheckedChange = actionRunCallback<ToggleAction>(
+ actionParametersOf(eventTargetKey to Buttons.SWITCH_2.name)
+ ),
text = "Switch 2",
style = textStyle,
modifier = fillModifier
@@ -126,13 +149,11 @@
@Composable
fun CountClicks() {
val prefs = currentState<Preferences>()
- val count = prefs[countClicksKey]
-
- val parameters = actionParametersOf(uiNameKey to LocalUiKey.current)
+ val count = prefs[countClicksKey] ?: 0
Row(modifier = GlanceModifier.fillMaxWidth()) {
Button(
text = "Count clicks",
- onClick = actionRunCallback<ClickAction>(parameters)
+ onClick = actionRunCallback<ClickAction>()
)
Text(text = "$count clicks")
}
@@ -140,36 +161,38 @@
class ClickAction : ActionCallback {
override suspend fun onRun(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
- val uiKey = requireNotNull(parameters[uiNameKey]) {
- "Add UI name to parameters, to access the view state."
- }
- GlanceState.updateValue(context, MyStateDefinition, uiKey) { prefs ->
+ updateAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) { prefs ->
prefs.toMutablePreferences().apply {
- this[countClicksKey] = prefs[countClicksKey]!! + 1
- }.toPreferences()
+ this[countClicksKey] = (prefs[countClicksKey] ?: 0) + 1
+ }
}
CompoundButtonAppWidget().update(context, glanceId)
}
}
-object MyStateDefinition : GlanceStateDefinition<Preferences> {
- override fun getLocation(context: Context, fileKey: String): File =
- context.preferencesDataStoreFile(fileKey)
+private val countClicksKey = intPreferencesKey("CountClicks")
- @Suppress("UNCHECKED_CAST")
- override suspend fun <T> getDataStore(context: Context, fileKey: String): DataStore<T> =
- PreferenceDataStoreFactory.create { context.preferencesDataStoreFile(fileKey) }
- .apply {
- edit { prefs ->
- if (prefs[countClicksKey] == null) {
- prefs[countClicksKey] = 0
- }
- }
- } as DataStore<T>
+class ToggleAction : ActionCallback {
+ override suspend fun onRun(
+ context: Context,
+ glanceId: GlanceId,
+ parameters: ActionParameters,
+ ) {
+ val target = requireNotNull(parameters[eventTargetKey]) {
+ "Add event target to parameters in order to update the view state."
+ }.let { CompoundButtonAppWidget.Buttons.valueOf(it) }
+ val checked = requireNotNull(parameters[ToggleableStateKey]) {
+ "This action should only be called in response to toggleable events"
+ }
+
+ updateAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) { prefs ->
+ prefs.toMutablePreferences().apply { set(target.prefsKey, checked) }
+ }
+ CompoundButtonAppWidget().update(context, glanceId)
+ }
}
-private val countClicksKey = intPreferencesKey("CountClicks")
-private val uiNameKey = ActionParameters.Key<String>("UiKey")
+private val eventTargetKey = ActionParameters.Key<String>("EventTarget")
class CompoundButtonAppWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget = CompoundButtonAppWidget()
diff --git a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ScrollableAppWidget.kt b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ScrollableAppWidget.kt
index d22c759..16c26a3 100644
--- a/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ScrollableAppWidget.kt
+++ b/glance/glance-appwidget/integration-tests/demos/src/main/java/androidx/glance/appwidget/demos/ScrollableAppWidget.kt
@@ -23,29 +23,35 @@
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
-import androidx.compose.material.Text as ComposeText
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.glance.Button
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.LocalContext
import androidx.glance.LocalSize
-import androidx.glance.appwidget.action.ActionCallback
import androidx.glance.action.ActionParameters
-import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.action.actionParametersOf
import androidx.glance.action.clickable
+import androidx.glance.appwidget.CheckBox
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import androidx.glance.appwidget.SizeMode
+import androidx.glance.appwidget.action.ActionCallback
+import androidx.glance.appwidget.action.ToggleableStateKey
import androidx.glance.appwidget.action.actionLaunchActivity
+import androidx.glance.appwidget.action.actionRunCallback
+import androidx.glance.appwidget.demos.ScrollableAppWidget.Companion.checkboxKey
import androidx.glance.appwidget.lazy.LazyColumn
import androidx.glance.appwidget.lazy.itemsIndexed
+import androidx.glance.appwidget.state.updateAppWidgetState
import androidx.glance.background
+import androidx.glance.currentState
import androidx.glance.layout.Alignment
import androidx.glance.layout.Column
import androidx.glance.layout.Row
@@ -53,11 +59,14 @@
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
+import androidx.glance.state.GlanceStateDefinition
+import androidx.glance.state.PreferencesGlanceStateDefinition
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextAlign
import androidx.glance.text.TextDecoration
import androidx.glance.text.TextStyle
+import androidx.compose.material.Text as ComposeText
/**
* Sample AppWidget that showcase scrollable layouts using the LazyColumn
@@ -68,8 +77,12 @@
private val singleColumn = DpSize(100.dp, 48.dp)
private val doubleColumn = DpSize(200.dp, 48.dp)
private val tripleColumn = DpSize(300.dp, 48.dp)
+
+ val checkboxKey = booleanPreferencesKey("checkbox")
}
+ override var stateDefinition: GlanceStateDefinition<*> = PreferencesGlanceStateDefinition
+
override val sizeMode: SizeMode = SizeMode.Responsive(
setOf(singleColumn, doubleColumn, tripleColumn)
)
@@ -105,6 +118,7 @@
@Composable
private fun ScrollColumn(modifier: GlanceModifier) {
+ val prefs = currentState<Preferences>()
LazyColumn(modifier) {
item {
SectionHeading(
@@ -161,6 +175,19 @@
)
)
}
+ item {
+ SectionHeading(
+ title = "Compound buttons",
+ description = "Check buttons below"
+ )
+ }
+ item {
+ CheckBox(
+ checked = prefs[checkboxKey] ?: false,
+ onCheckedChange = actionRunCallback<ListToggleAction>(),
+ text = "Checkbox"
+ )
+ }
}
}
@@ -208,6 +235,20 @@
}
}
+class ListToggleAction : ActionCallback {
+ override suspend fun onRun(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
+ ScrollableAppWidget().run {
+ updateAppWidgetState<Preferences>(context, glanceId) { prefs ->
+ prefs.toMutablePreferences().apply {
+ val checked = parameters[ToggleableStateKey] ?: false
+ set(checkboxKey, checked)
+ }
+ }
+ update(context, glanceId)
+ }
+ }
+}
+
private val ClickedItemKey = ActionParameters.Key<Int>("ClickedItemKey")
class ScrollableAppWidgetReceiver : GlanceAppWidgetReceiver() {
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
index 0a187b8..981a4cb 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostRule.kt
@@ -85,12 +85,15 @@
private val mInnerRules = RuleChain.outerRule(mActivityRule).around(mOrientationRule)
private var mHostStarted = false
- lateinit var mHostView: TestAppWidgetHostView
+ private var mMaybeHostView: TestAppWidgetHostView? = null
private var mAppWidgetId = 0
private val mScenario: ActivityScenario<AppWidgetHostTestActivity>
get() = mActivityRule.scenario
private val mContext = ApplicationProvider.getApplicationContext<Context>()
+ val mHostView: TestAppWidgetHostView
+ get() = checkNotNull(mMaybeHostView) { "No app widget installed on the host" }
+
override fun apply(base: Statement, description: Description) = object : Statement() {
override fun evaluate() {
@@ -111,12 +114,21 @@
mHostStarted = true
mActivityRule.scenario.onActivity { activity ->
- mHostView = activity.bindAppWidget(mPortraitSize, mLandscapeSize)
+ mMaybeHostView = activity.bindAppWidget(mPortraitSize, mLandscapeSize)
}
+ val hostView = checkNotNull(mMaybeHostView) { "Host view wasn't successfully started" }
+
runAndWaitForChildren {
- mAppWidgetId = mHostView.appWidgetId
- mHostView.waitForRemoteViews()
+ mAppWidgetId = hostView.appWidgetId
+ hostView.waitForRemoteViews()
+ }
+ }
+
+ fun removeAppWidget() {
+ mActivityRule.scenario.onActivity { activity ->
+ val hostView = checkNotNull(mMaybeHostView) { "No app widget to remove" }
+ activity.deleteAppWidget(hostView)
}
}
@@ -182,18 +194,21 @@
mPortraitSize = portrait
if (!mHostStarted) return
- mScenario.onActivity {
- mHostView.setSizes(portrait, landscape)
- }
+ val hostView = mMaybeHostView
+ if (hostView != null) {
+ mScenario.onActivity {
+ hostView.setSizes(portrait, landscape)
+ }
- if (updateRemoteViews) {
- runAndWaitForChildren {
- mHostView.resetRemoteViewsLatch()
- AppWidgetManager.getInstance(mContext).updateAppWidgetOptions(
- mAppWidgetId,
- optionsBundleOf(listOf(portrait, landscape))
- )
- mHostView.waitForRemoteViews()
+ if (updateRemoteViews) {
+ runAndWaitForChildren {
+ hostView.resetRemoteViewsLatch()
+ AppWidgetManager.getInstance(mContext).updateAppWidgetOptions(
+ mAppWidgetId,
+ optionsBundleOf(listOf(portrait, landscape))
+ )
+ hostView.waitForRemoteViews()
+ }
}
}
}
@@ -203,12 +218,13 @@
run: () -> Unit = {},
test: () -> Boolean
) {
+ val hostView = mHostView
val latch = CountDownLatch(1)
val onDrawListener = ViewTreeObserver.OnDrawListener {
- if (mHostView.childCount > 0 && test()) latch.countDown()
+ if (hostView.childCount > 0 && test()) latch.countDown()
}
mActivityRule.scenario.onActivity {
- mHostView.viewTreeObserver.addOnDrawListener(onDrawListener)
+ hostView.viewTreeObserver.addOnDrawListener(onDrawListener)
}
run()
@@ -224,7 +240,7 @@
} finally {
latch.countDown() // make sure it's released in all conditions
mActivityRule.scenario.onActivity {
- mHostView.viewTreeObserver.removeOnDrawListener(onDrawListener)
+ hostView.viewTreeObserver.removeOnDrawListener(onDrawListener)
}
}
}
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostTestActivity.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostTestActivity.kt
index a5b1fab..97f6c7d 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostTestActivity.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/AppWidgetHostTestActivity.kt
@@ -101,6 +101,14 @@
return hostView
}
+ fun deleteAppWidget(hostView: TestAppWidgetHostView) {
+ val appWidgetId = hostView.appWidgetId
+ mHost?.deleteAppWidgetId(appWidgetId)
+ mHostViews.remove(hostView)
+ val contentFrame = findViewById<FrameLayout>(R.id.content)
+ contentFrame.removeView(hostView)
+ }
+
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
updateAllSizes(newConfig.orientation)
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/CheckBoxTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/CheckBoxTest.kt
index a28888a..29ea48be 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/CheckBoxTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/CheckBoxTest.kt
@@ -47,13 +47,13 @@
private val checkedCheckBox: @Composable () -> Unit = {
Box {
- CheckBox(checked = true, text = "Hello world")
+ CheckBox(checked = true, onCheckedChange = null, text = "Hello world")
}
}
private val uncheckedCheckBox: @Composable () -> Unit = {
Box {
- CheckBox(checked = false, text = "Hola mundo")
+ CheckBox(checked = false, onCheckedChange = null, text = "Hola mundo")
}
}
@@ -127,7 +127,10 @@
fun check_box_modifiers() {
TestGlanceAppWidget.uiDefinition = {
Box {
- CheckBox(checked = true, modifier = GlanceModifier.padding(5.dp, 6.dp, 7.dp, 8.dp))
+ CheckBox(
+ checked = true,
+ onCheckedChange = null,
+ modifier = GlanceModifier.padding(5.dp, 6.dp, 7.dp, 8.dp))
}
}
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt
index 5fe5b3a..d8dde64 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverScreenshotTest.kt
@@ -224,12 +224,14 @@
Row(modifier = GlanceModifier.defaultWeight().fillMaxWidth()) {
CheckBox(
checked = false,
+ onCheckedChange = null,
text = "Start",
modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
style = TextStyle(textAlign = TextAlign.Start)
)
CheckBox(
checked = true,
+ onCheckedChange = null,
text = "End",
modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
style = TextStyle(textAlign = TextAlign.End)
@@ -238,12 +240,14 @@
Row(modifier = GlanceModifier.defaultWeight().fillMaxWidth()) {
Switch(
checked = false,
+ onCheckedChange = null,
text = "Start",
modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
style = TextStyle(textAlign = TextAlign.Start)
)
Switch(
checked = true,
+ onCheckedChange = null,
text = "End",
modifier = GlanceModifier.defaultWeight().fillMaxHeight(),
style = TextStyle(textAlign = TextAlign.End)
@@ -582,6 +586,7 @@
Column(modifier = GlanceModifier.background(day = Color.White, night = Color.Black)) {
CheckBox(
checked = true,
+ onCheckedChange = null,
text = "Hello Checked Checkbox (text: day=black, night=white| box: day=magenta, " +
"night=yellow)",
style = TextStyle(
@@ -596,6 +601,7 @@
CheckBox(
checked = false,
+ onCheckedChange = null,
text = "Hello Unchecked Checkbox (text: day=dark gray, night=light gray, green box)",
style = TextStyle(
color = ColorProvider(day = Color.DarkGray, night = Color.LightGray),
@@ -613,6 +619,7 @@
Column(modifier = GlanceModifier.background(day = Color.White, night = Color.Black)) {
Switch(
checked = true,
+ onCheckedChange = null,
text = "Hello Checked Switch (day: Blue/Green, night: Red/Yellow)",
style = TextStyle(
color = ColorProvider(day = Color.Black, night = Color.White),
@@ -627,6 +634,7 @@
Switch(
checked = false,
+ onCheckedChange = null,
text = "Hello Unchecked Switch",
style = TextStyle(
color = ColorProvider(day = Color.Black, night = Color.White),
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt
index bbf99f3..65e7965 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiverTest.kt
@@ -34,15 +34,21 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.intPreferencesKey
import androidx.glance.Button
+import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.LocalContext
import androidx.glance.LocalSize
import androidx.glance.action.actionLaunchActivity
+import androidx.glance.appwidget.state.getAppWidgetState
+import androidx.glance.appwidget.state.updateAppWidgetState
import androidx.glance.appwidget.test.R
import androidx.glance.background
+import androidx.glance.currentState
import androidx.glance.layout.Box
import androidx.glance.layout.Column
import androidx.glance.layout.ContentScale
@@ -53,6 +59,7 @@
import androidx.glance.layout.height
import androidx.glance.layout.width
import androidx.glance.layout.wrapContentHeight
+import androidx.glance.state.PreferencesGlanceStateDefinition
import androidx.glance.text.FontStyle
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
@@ -60,10 +67,17 @@
import androidx.glance.text.TextStyle
import androidx.test.filters.MediumTest
import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicReference
import kotlin.test.assertIs
import kotlin.test.assertNotNull
@@ -73,6 +87,8 @@
@get:Rule
val mHostRule = AppWidgetHostRule()
+ val context = InstrumentationRegistry.getInstrumentation().targetContext!!
+
@Before
fun setUp() {
// Reset the size mode to the default
@@ -432,7 +448,7 @@
@Test
fun bitmapBackground() {
- TestGlanceAppWidget.uiDefinition = compose@{
+ TestGlanceAppWidget.uiDefinition = {
val context = LocalContext.current
val bitmap =
(context.resources.getDrawable(R.drawable.compose, null) as BitmapDrawable).bitmap
@@ -455,8 +471,156 @@
}
}
+ @Test
+ fun removeAppWidget() {
+ TestGlanceAppWidget.stateDefinition = PreferencesGlanceStateDefinition
+ TestGlanceAppWidget.uiDefinition = {
+ Text("something")
+ }
+
+ mHostRule.startHost()
+
+ val appWidgetManager = GlanceAppWidgetManager(context)
+ val glanceId = runBlocking {
+ appWidgetManager.getGlanceIds(TestGlanceAppWidget::class.java).single()
+ }
+
+ runBlocking {
+ TestGlanceAppWidget.updateAppWidgetState<Preferences>(context, glanceId) { prefs ->
+ prefs.toMutablePreferences().apply {
+ this[testKey] = 3
+ }
+ }
+ }
+
+ val fileKey = createUniqueRemoteUiName((glanceId as AppWidgetId).appWidgetId)
+ val preferencesFile = PreferencesGlanceStateDefinition.getLocation(context, fileKey)
+
+ assertThat(preferencesFile.exists())
+
+ val deleteLatch = CountDownLatch(1)
+ TestGlanceAppWidget.setOnDeleteBlock {
+ deleteLatch.countDown()
+ }
+
+ mHostRule.removeAppWidget()
+
+ deleteLatch.await(5, TimeUnit.SECONDS)
+ val interval = 200L
+ for (timeout in 0..2000L step interval) {
+ if (!preferencesFile.exists()) return
+ Thread.sleep(interval)
+ }
+ assertWithMessage("View state file exists").that(preferencesFile.exists())
+ .isFalse()
+ }
+
+ @Test
+ fun updateAll() {
+ TestGlanceAppWidget.uiDefinition = {
+ Text("before")
+ }
+
+ mHostRule.startHost()
+
+ val didRun = AtomicBoolean(false)
+ TestGlanceAppWidget.uiDefinition = {
+ didRun.set(true)
+ Text("after")
+ }
+
+ runBlocking {
+ TestGlanceAppWidget.updateAll(context)
+ }
+ assertThat(didRun.get()).isTrue()
+ }
+
+ @Test
+ fun updateIf() {
+ TestGlanceAppWidget.stateDefinition = PreferencesGlanceStateDefinition
+
+ TestGlanceAppWidget.uiDefinition = {
+ Text("before")
+ }
+
+ mHostRule.startHost()
+
+ val appWidgetManager = GlanceAppWidgetManager(context)
+ runBlocking {
+ appWidgetManager.getGlanceIds(TestGlanceAppWidget::class.java)
+ .forEach { glanceId ->
+ updateAppWidgetState(
+ context,
+ PreferencesGlanceStateDefinition,
+ glanceId
+ ) { prefs ->
+ prefs.toMutablePreferences().apply {
+ this[testKey] = 2
+ }
+ }
+ }
+ }
+
+ // Make sure the app widget is updated if the test is true
+ val didRun = AtomicBoolean(false)
+ TestGlanceAppWidget.uiDefinition = {
+ didRun.set(true)
+ Text("after")
+ }
+ runBlocking {
+ TestGlanceAppWidget.updateIf<Preferences>(context) { prefs ->
+ prefs[testKey] == 2
+ }
+ }
+
+ assertThat(didRun.get()).isTrue()
+
+ // Make sure it is not if the test is false
+ didRun.set(false)
+ runBlocking {
+ TestGlanceAppWidget.updateIf<Preferences>(context) { prefs ->
+ prefs[testKey] == 3
+ }
+ }
+
+ assertThat(didRun.get()).isFalse()
+ }
+
+ @Test
+ fun viewState() {
+ TestGlanceAppWidget.stateDefinition = PreferencesGlanceStateDefinition
+
+ TestGlanceAppWidget.uiDefinition = {
+ val value = currentState<Preferences>()[testKey] ?: -1
+ Text("Value = $value")
+ }
+
+ mHostRule.startHost()
+
+ val appWidgetId = AtomicReference<GlanceId>()
+ mHostRule.onHostView { view ->
+ appWidgetId.set(AppWidgetId(view.appWidgetId))
+ }
+
+ runBlocking {
+ TestGlanceAppWidget.updateAppWidgetState<Preferences>(
+ context,
+ appWidgetId.get()
+ ) { prefs ->
+ prefs.toMutablePreferences().apply {
+ this[testKey] = 2
+ }
+ }
+
+ val prefs =
+ TestGlanceAppWidget.getAppWidgetState<Preferences>(context, appWidgetId.get())
+ assertThat(prefs[testKey]).isEqualTo(2)
+ }
+ }
+
// Check there is a single span of the given type and that it passes the [check].
- private inline fun <reified T> SpannedString.checkHasSingleTypedSpan(check: (T) -> Unit) {
+ private inline
+ fun <reified T> SpannedString.checkHasSingleTypedSpan(check: (T) -> Unit) {
val spans = getSpans(0, length, T::class.java)
assertThat(spans).hasLength(1)
check(spans[0])
@@ -464,8 +628,10 @@
private fun assertViewSize(view: View, expectedSize: DpSize) {
val density = view.context.resources.displayMetrics.density
- assertThat(view.width / density).isWithin(1.1f / density).of(expectedSize.width.value)
- assertThat(view.height / density).isWithin(1.1f / density).of(expectedSize.height.value)
+ assertThat(view.width / density).isWithin(1.1f / density)
+ .of(expectedSize.width.value)
+ assertThat(view.height / density).isWithin(1.1f / density)
+ .of(expectedSize.height.value)
}
private fun assertViewDimension(view: View, sizePx: Int, expectedSize: Dp) {
@@ -473,3 +639,5 @@
assertThat(sizePx / density).isWithin(1.1f / density).of(expectedSize.value)
}
}
+
+private val testKey = intPreferencesKey("testKey")
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/LazyColumnTest.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/LazyColumnTest.kt
index c4ae56e..23acd1a 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/LazyColumnTest.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/LazyColumnTest.kt
@@ -362,8 +362,8 @@
private inline fun <reified T : View> ListView.getUnboxedListItem(position: Int): T {
val remoteViewFrame = assertIs<FrameLayout>(getChildAt(position))
- // Android R- have a RemoteViewsAdapter$RemoteViewsFrameLayout first, Android S+ do not.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ // Android S- have a RemoteViewsAdapter$RemoteViewsFrameLayout first, Android T+ do not.
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S) {
return remoteViewFrame.getChildAt(0).getTargetView()
}
val frame = assertIs<FrameLayout>(remoteViewFrame.getChildAt(0))
diff --git a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/TestGlanceAppWidgetReceiver.kt b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/TestGlanceAppWidgetReceiver.kt
index 5f17899..12ac3f0 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/TestGlanceAppWidgetReceiver.kt
+++ b/glance/glance-appwidget/src/androidAndroidTest/kotlin/androidx/glance/appwidget/TestGlanceAppWidgetReceiver.kt
@@ -17,6 +17,8 @@
package androidx.glance.appwidget
import androidx.compose.runtime.Composable
+import androidx.glance.GlanceId
+import androidx.glance.state.GlanceStateDefinition
class TestGlanceAppWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = TestGlanceAppWidget
@@ -24,6 +26,8 @@
object TestGlanceAppWidget : GlanceAppWidget(errorUiLayout = 0) {
+ override var stateDefinition: GlanceStateDefinition<*>? = null
+
override var sizeMode: SizeMode = SizeMode.Single
@Composable
@@ -31,5 +35,19 @@
uiDefinition()
}
+ private var onDeleteBlock: ((GlanceId) -> Unit)? = null
+
+ fun setOnDeleteBlock(block: (GlanceId) -> Unit) {
+ onDeleteBlock = block
+ }
+
+ fun resetOnDeleteBlock() {
+ onDeleteBlock = null
+ }
+
+ override suspend fun onDelete(glanceId: GlanceId) {
+ onDeleteBlock?.apply { this(glanceId) }
+ }
+
var uiDefinition: @Composable () -> Unit = { }
}
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml b/glance/glance-appwidget/src/androidAndroidTest/res/values-v29/themes.xml
similarity index 79%
copy from glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
copy to glance/glance-appwidget/src/androidAndroidTest/res/values-v29/themes.xml
index 987c157..1a85e18 100644
--- a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
+++ b/glance/glance-appwidget/src/androidAndroidTest/res/values-v29/themes.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/abc_btn_switch_to_on_mtrl_00012"
- android:insetRight="18dp"/>
\ No newline at end of file
+<resources>
+ <style name="Glance.AppWidget.Theme" parent=""/>
+</resources>
diff --git a/glance/glance-appwidget/src/androidAndroidTest/res/values/themes.xml b/glance/glance-appwidget/src/androidAndroidTest/res/values/themes.xml
index efd9192..2f50199 100644
--- a/glance/glance-appwidget/src/androidAndroidTest/res/values/themes.xml
+++ b/glance/glance-appwidget/src/androidAndroidTest/res/values/themes.xml
@@ -18,4 +18,5 @@
<style name="Theme.TestDimensionAttr" parent="">
<item name="dimensionAttr">@dimen/testDimension</item>
</style>
+ <style name="Glance.AppWidget.Theme" parent=""/>
</resources>
diff --git a/glance/glance-appwidget/src/androidMain/AndroidManifest.xml b/glance/glance-appwidget/src/androidMain/AndroidManifest.xml
index 579156f..9ca4dbf 100644
--- a/glance/glance-appwidget/src/androidMain/AndroidManifest.xml
+++ b/glance/glance-appwidget/src/androidMain/AndroidManifest.xml
@@ -18,11 +18,11 @@
package="androidx.glance.appwidget" >
<application>
<activity
- android:name=".ListAdapterLaunchActivityTrampolineActivity"
+ android:name=".ListAdapterTrampolineActivity"
android:exported="false"
android:enabled="true" />
<activity
- android:name=".ListAdapterCallbackTrampolineActivity"
+ android:name=".ListAdapterInvisibleTrampolineActivity"
android:exported="false"
android:taskAffinity="androidx.glance.appwidget.ListAdapterCallbackTrampoline"
android:theme="@style/Widget.Glance.AppWidget.CallbackTrampoline"
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ApplyModifiers.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ApplyModifiers.kt
index cbb494c..16861ac 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ApplyModifiers.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ApplyModifiers.kt
@@ -19,7 +19,6 @@
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.net.Uri
import android.os.Build
import android.util.Log
import android.util.TypedValue.COMPLEX_UNIT_DIP
@@ -32,6 +31,7 @@
import androidx.annotation.IdRes
import androidx.annotation.RequiresApi
import androidx.compose.ui.graphics.toArgb
+import androidx.core.os.bundleOf
import androidx.core.widget.RemoteViewsCompat.setTextViewHeight
import androidx.core.widget.RemoteViewsCompat.setTextViewWidth
import androidx.core.widget.RemoteViewsCompat.setViewBackgroundColor
@@ -45,17 +45,28 @@
import androidx.glance.VisibilityModifier
import androidx.glance.action.Action
import androidx.glance.action.ActionModifier
+import androidx.glance.action.ActionParameters
import androidx.glance.action.LaunchActivityAction
import androidx.glance.action.LaunchActivityClassAction
import androidx.glance.action.LaunchActivityComponentAction
-import androidx.glance.appwidget.ListAdapterLaunchActivityTrampolineActivity.Companion.putActivityIntentExtra
-import androidx.glance.appwidget.action.RunCallbackAction
+import androidx.glance.action.toMutableParameters
+import androidx.glance.appwidget.action.CompoundButtonAction
import androidx.glance.appwidget.action.LaunchActivityIntentAction
+import androidx.glance.appwidget.action.LaunchBroadcastReceiverAction
+import androidx.glance.appwidget.action.LaunchBroadcastReceiverActionAction
+import androidx.glance.appwidget.action.LaunchBroadcastReceiverClassAction
+import androidx.glance.appwidget.action.LaunchBroadcastReceiverComponentAction
+import androidx.glance.appwidget.action.LaunchBroadcastReceiverIntentAction
+import androidx.glance.appwidget.action.LaunchServiceAction
+import androidx.glance.appwidget.action.LaunchServiceClassAction
+import androidx.glance.appwidget.action.LaunchServiceComponentAction
+import androidx.glance.appwidget.action.LaunchServiceIntentAction
+import androidx.glance.appwidget.action.RunCallbackAction
+import androidx.glance.appwidget.action.ToggleableStateKey
import androidx.glance.appwidget.unit.DayNightColorProvider
import androidx.glance.layout.HeightModifier
import androidx.glance.layout.PaddingModifier
import androidx.glance.layout.WidthModifier
-import androidx.glance.appwidget.ListAdapterCallbackTrampolineActivity.Companion.putBroadcastIntentExtra
import androidx.glance.unit.Dimension
import androidx.glance.unit.FixedColorProvider
import androidx.glance.unit.ResourceColorProvider
@@ -84,6 +95,9 @@
}
is VisibilityModifier -> visibility = modifier.visibility
is CornerRadiusModifier -> cornerRadius = modifier.radius
+ is AppWidgetBackgroundModifier -> {
+ // This modifier is handled somewhere else.
+ }
else -> {
Log.w(GlanceAppWidgetTag, "Unknown modifier '$modifier', nothing done.")
}
@@ -118,89 +132,200 @@
action: Action,
@IdRes viewId: Int
) {
+ try {
+ if (translationContext.isLazyCollectionDescendant) {
+ val fillInIntent = getFillInIntentForAction(action, translationContext, viewId)
+ if (action is CompoundButtonAction && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ ApplyModifiersApi31Impl.setOnCheckedChangeResponse(rv, viewId, fillInIntent)
+ } else {
+ rv.setOnClickFillInIntent(viewId, fillInIntent)
+ }
+ } else {
+ val pendingIntent = getPendingIntentForAction(action, translationContext)
+ if (action is CompoundButtonAction && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ ApplyModifiersApi31Impl.setOnCheckedChangeResponse(rv, viewId, pendingIntent)
+ } else {
+ rv.setOnClickPendingIntent(viewId, pendingIntent)
+ }
+ }
+ } catch (t: Throwable) {
+ Log.e(GlanceAppWidgetTag, "Unrecognized Action: $action", t)
+ }
+}
+
+private fun getPendingIntentForAction(
+ action: Action,
+ translationContext: TranslationContext,
+ editParams: (ActionParameters) -> ActionParameters = { it }
+): PendingIntent {
when (action) {
is LaunchActivityAction -> {
- val activityIntent = when (action) {
- is LaunchActivityComponentAction -> Intent().setComponent(action.componentName)
- is LaunchActivityClassAction ->
- Intent(translationContext.context, action.activityClass)
- is LaunchActivityIntentAction -> action.intent
- else -> error("Action type not defined in app widget package: $action")
- }
-
- if (translationContext.isLazyCollectionDescendant) {
- val fillIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- // Fill in the pending intent template with the action intent directly, with a
- // unique identifier to ensure unique filterEquals
- ApplyModifiersApiApi29Impl.setIntentIdentifier(activityIntent, viewId)
- } else {
- // Send the action intent to an activity trampoline, where it will be invoked,
- // unmodified
- Intent(
- translationContext.context,
- ListAdapterLaunchActivityTrampolineActivity::class.java
- ).apply {
- data = Uri.parse(toUri(0))
- .buildUpon()
- .scheme("startActivityAction")
- .path(viewId.toString())
- .build()
- putActivityIntentExtra(activityIntent)
- }
- }
- rv.setOnClickFillInIntent(viewId, fillIntent)
+ return PendingIntent.getActivity(
+ translationContext.context,
+ 0,
+ getLaunchActivityIntent(action, translationContext, editParams),
+ PendingIntent.FLAG_MUTABLE
+ )
+ }
+ is LaunchServiceAction -> {
+ val intent = getLaunchServiceIntent(action, translationContext)
+ return if (action.isForegroundService &&
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+ ) {
+ ApplyModifiersApi26Impl.getForegroundServicePendingIntent(
+ context = translationContext.context,
+ intent = intent
+ )
} else {
- val pendingIntent: PendingIntent =
- PendingIntent.getActivity(
- translationContext.context,
- 0,
- activityIntent,
- PendingIntent.FLAG_MUTABLE
- )
-
- rv.setOnClickPendingIntent(viewId, pendingIntent)
+ PendingIntent.getService(
+ translationContext.context,
+ 0,
+ intent,
+ PendingIntent.FLAG_MUTABLE
+ )
}
}
+ is LaunchBroadcastReceiverAction -> {
+ return PendingIntent.getBroadcast(
+ translationContext.context,
+ 0,
+ getLaunchBroadcastReceiverIntent(action, translationContext),
+ PendingIntent.FLAG_MUTABLE
+ )
+ }
is RunCallbackAction -> {
- if (translationContext.isLazyCollectionDescendant) {
- val actionIntent = ActionCallbackBroadcastReceiver.createIntent(
- translationContext.context,
- action.callbackClass,
- translationContext.appWidgetId,
- action.parameters
- )
- rv.setOnClickFillInIntent(
- viewId,
- Intent(
- translationContext.context,
- ListAdapterCallbackTrampolineActivity::class.java
- ).apply {
- data = Uri.parse(toUri(0))
- .buildUpon()
- .scheme("updateContentAction")
- .path(viewId.toString())
- .build()
- putBroadcastIntentExtra(actionIntent)
- }
- )
- } else {
- val pendingIntent =
- ActionCallbackBroadcastReceiver.createPendingIntent(
- translationContext.context,
- action.callbackClass,
- translationContext.appWidgetId,
- action.parameters
- )
- rv.setOnClickPendingIntent(viewId, pendingIntent)
- }
+ return ActionCallbackBroadcastReceiver.createPendingIntent(
+ translationContext.context,
+ action.callbackClass,
+ translationContext.appWidgetId,
+ editParams(action.parameters)
+ )
}
- else -> {
- Log.e(
- GlanceAppWidgetTag,
- "Unrecognized action type: ${action.javaClass.canonicalName}."
+ is CompoundButtonAction -> {
+ return getPendingIntentForAction(
+ action.innerAction,
+ translationContext,
+ action.getActionParameters()
+ )
+ }
+ else -> error("Cannot create PendingIntent for action type: $action")
+ }
+}
+
+private fun getFillInIntentForAction(
+ action: Action,
+ translationContext: TranslationContext,
+ @IdRes viewId: Int,
+ editParams: (ActionParameters) -> ActionParameters = { it }
+): Intent = when (action) {
+ is LaunchActivityAction -> {
+ val launchActivityIntent = getLaunchActivityIntent(
+ action = action,
+ translationContext = translationContext,
+ editParams = editParams
+ )
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ // Fill in the pending intent template with the action intent directly, with a
+ // unique identifier to ensure unique filterEquals
+ ApplyModifiersApi29Impl.setIntentIdentifier(launchActivityIntent, viewId)
+ } else {
+ launchActivityIntent.applyTrampolineIntent(
+ context = translationContext.context,
+ viewId = viewId,
+ type = ListAdapterTrampolineType.ACTIVITY
)
}
}
+ is LaunchServiceAction -> getLaunchServiceIntent(
+ action = action,
+ translationContext = translationContext
+ ).applyTrampolineIntent(
+ context = translationContext.context,
+ viewId = viewId,
+ type = if (action.isForegroundService) {
+ ListAdapterTrampolineType.FOREGROUND_SERVICE
+ } else {
+ ListAdapterTrampolineType.SERVICE
+ }
+ )
+ is LaunchBroadcastReceiverAction -> getLaunchBroadcastReceiverIntent(
+ action = action,
+ translationContext = translationContext
+ ).applyTrampolineIntent(
+ context = translationContext.context,
+ viewId = viewId,
+ type = ListAdapterTrampolineType.BROADCAST
+ )
+ is RunCallbackAction -> ActionCallbackBroadcastReceiver.createIntent(
+ context = translationContext.context,
+ callbackClass = action.callbackClass,
+ appWidgetId = translationContext.appWidgetId,
+ parameters = editParams(action.parameters)
+ ).applyTrampolineIntent(
+ context = translationContext.context,
+ viewId = viewId,
+ type = ListAdapterTrampolineType.BROADCAST
+ )
+ is CompoundButtonAction -> getFillInIntentForAction(
+ action.innerAction,
+ translationContext,
+ viewId,
+ action.getActionParameters()
+ )
+ else -> error("Cannot create fill-in Intent for action type: $action")
+}
+
+private fun CompoundButtonAction.getActionParameters() = { params: ActionParameters ->
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+ params.toMutableParameters().apply {
+ set(ToggleableStateKey, !checked)
+ }
+ } else {
+ params
+ }
+}
+
+private fun getLaunchBroadcastReceiverIntent(
+ action: LaunchBroadcastReceiverAction,
+ translationContext: TranslationContext
+) = when (action) {
+ is LaunchBroadcastReceiverComponentAction -> Intent().setComponent(action.componentName)
+ is LaunchBroadcastReceiverClassAction ->
+ Intent(translationContext.context, action.receiverClass)
+ is LaunchBroadcastReceiverIntentAction -> action.intent
+ is LaunchBroadcastReceiverActionAction ->
+ Intent(action.action).setComponent(action.componentName)
+}
+
+private fun getLaunchServiceIntent(
+ action: LaunchServiceAction,
+ translationContext: TranslationContext
+): Intent = when (action) {
+ is LaunchServiceComponentAction -> Intent().setComponent(action.componentName)
+ is LaunchServiceClassAction ->
+ Intent(translationContext.context, action.serviceClass)
+ is LaunchServiceIntentAction -> action.intent
+}
+
+private fun getLaunchActivityIntent(
+ action: LaunchActivityAction,
+ translationContext: TranslationContext,
+ editParams: (ActionParameters) -> ActionParameters = { it }
+): Intent {
+ val activityIntent = when (action) {
+ is LaunchActivityComponentAction -> Intent().setComponent(action.componentName)
+ is LaunchActivityClassAction ->
+ Intent(translationContext.context, action.activityClass)
+ is LaunchActivityIntentAction -> action.intent
+ else -> error("Action type not defined in app widget package: $action")
+ }
+
+ val parametersPairs = editParams(action.parameters).asMap().map { (key, value) ->
+ key.name to value
+ }.toTypedArray()
+
+ activityIntent.putExtras(bundleOf(*parametersPairs))
+ return activityIntent
}
private fun applySizeModifiers(
@@ -409,12 +534,35 @@
else -> error("Rounded corners should not be ${radius.javaClass.canonicalName}")
}
}
+
+ @DoNotInline
+ fun setOnCheckedChangeResponse(rv: RemoteViews, viewId: Int, intent: PendingIntent) {
+ rv.setOnCheckedChangeResponse(viewId, RemoteViews.RemoteResponse.fromPendingIntent(intent))
+ }
+
+ @DoNotInline
+ fun setOnCheckedChangeResponse(rv: RemoteViews, viewId: Int, intent: Intent) {
+ rv.setOnCheckedChangeResponse(viewId, RemoteViews.RemoteResponse.fromFillInIntent(intent))
+ }
}
@RequiresApi(Build.VERSION_CODES.Q)
-private object ApplyModifiersApiApi29Impl {
+private object ApplyModifiersApi29Impl {
@DoNotInline
fun setIntentIdentifier(intent: Intent, viewId: Int): Intent = intent.apply {
identifier = viewId.toString()
}
}
+
+@RequiresApi(Build.VERSION_CODES.O)
+private object ApplyModifiersApi26Impl {
+ @DoNotInline
+ fun getForegroundServicePendingIntent(context: Context, intent: Intent): PendingIntent {
+ return PendingIntent.getForegroundService(
+ context,
+ 0,
+ intent,
+ PendingIntent.FLAG_MUTABLE
+ )
+ }
+}
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CheckBox.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CheckBox.kt
index bcffc17..c00b526 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CheckBox.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CheckBox.kt
@@ -22,6 +22,9 @@
import androidx.glance.Emittable
import androidx.glance.GlanceModifier
import androidx.glance.GlanceNode
+import androidx.glance.action.Action
+import androidx.glance.action.ActionModifier
+import androidx.glance.appwidget.action.CompoundButtonAction
import androidx.glance.appwidget.unit.CheckableColorProvider
import androidx.glance.appwidget.unit.CheckedUncheckedColorProvider.Companion.createCheckableColorProvider
import androidx.glance.appwidget.unit.ResourceCheckableColorProvider
@@ -100,6 +103,10 @@
* Adds a check box view to the glance view.
*
* @param checked whether the check box is checked
+ * @param onCheckedChange the action to be run when the checkbox is clicked. The current value of
+ * checked is provided to this action in its ActionParameters, and can be retrieved using the
+ * [ToggleableStateKey]. If this action launches an activity, the current value of checked will be
+ * passed as an intent extra with the name [RemoteViews.EXTRA_CHECKED].
* @param modifier the modifier to apply to the check box
* @param text the text to display to the end of the check box
* @param style the style to apply to [text]
@@ -108,17 +115,23 @@
@Composable
public fun CheckBox(
checked: Boolean,
+ onCheckedChange: Action?,
modifier: GlanceModifier = GlanceModifier,
text: String = "",
style: TextStyle? = null,
colors: CheckBoxColors = CheckBoxDefaults.colors
) {
+ val finalModifier = if (onCheckedChange != null) {
+ modifier.then(ActionModifier(CompoundButtonAction(onCheckedChange, checked)))
+ } else {
+ modifier
+ }
GlanceNode(
factory = ::EmittableCheckBox,
update = {
this.set(checked) { this.checked = it }
this.set(text) { this.text = it }
- this.set(modifier) { this.modifier = it }
+ this.set(finalModifier) { this.modifier = it }
this.set(style) { this.style = it }
this.set(colors) { this.colors = it }
}
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CompositionLocals.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CompositionLocals.kt
index 863af1c..d812e74 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CompositionLocals.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/CompositionLocals.kt
@@ -19,7 +19,6 @@
import android.os.Bundle
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.staticCompositionLocalOf
-import androidx.glance.state.GlanceStateDefinition
/**
* Option Bundle accessible when generating an App Widget.
@@ -28,10 +27,3 @@
*/
public val LocalAppWidgetOptions: ProvidableCompositionLocal<Bundle> =
staticCompositionLocalOf { error("No default app widget options") }
-
-/**
- * Uniquely identifying key for this remote UI, for use with [GlanceStateDefinition].
- */
-// TODO: Look into refactoring the state to not need this key constant
-public val LocalUiKey =
- staticCompositionLocalOf<String> { error("No default UI key") }
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
index d49d35f..05336fd 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
@@ -19,7 +19,6 @@
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.content.Context
-import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.util.DisplayMetrics
@@ -43,6 +42,7 @@
import androidx.glance.LocalGlanceId
import androidx.glance.LocalSize
import androidx.glance.LocalState
+import androidx.glance.appwidget.state.getAppWidgetState
import kotlinx.coroutines.CancellationException
import androidx.glance.state.GlanceState
import androidx.glance.state.GlanceStateDefinition
@@ -85,6 +85,13 @@
public open val stateDefinition: GlanceStateDefinition<*>? = null
/**
+ * Method called by the framework when an App Widget has been removed from its host.
+ *
+ * When the method returns, the state associated with the [glanceId] will be deleted.
+ */
+ public open suspend fun onDelete(glanceId: GlanceId) { }
+
+ /**
* Triggers the composition of [Content] and sends the result to the [AppWidgetManager].
*/
public suspend fun update(context: Context, glanceId: GlanceId) {
@@ -95,6 +102,26 @@
}
/**
+ * Calls [onDelete], then clear local data associated with the [appWidgetId].
+ *
+ * This is meant to be called when the App Widget instance has been deleted from the host.
+ */
+ internal suspend fun deleted(context: Context, appWidgetId: Int) {
+ val glanceId = AppWidgetId(appWidgetId)
+ try {
+ onDelete(glanceId)
+ } catch (cancelled: CancellationException) {
+ // Nothing to do here
+ } catch (t: Throwable) {
+ Log.e(GlanceAppWidgetTag, "Error in user-provided deletion callback", t)
+ } finally {
+ stateDefinition?.let {
+ GlanceState.deleteStore(context, it, createUniqueRemoteUiName(appWidgetId))
+ }
+ }
+ }
+
+ /**
* Internal version of [update], to be used by the broadcast receiver directly.
*/
internal suspend fun update(
@@ -348,13 +375,11 @@
val recomposer = Recomposer(coroutineContext)
val composition = Composition(applier, recomposer)
val glanceId = AppWidgetId(appWidgetId)
- val uiKey = createUniqueRemoteUiName(appWidgetId)
composition.setContent {
CompositionLocalProvider(
LocalContext provides context,
LocalGlanceId provides glanceId,
LocalAppWidgetOptions provides options,
- LocalUiKey provides uiKey,
LocalState provides state,
LocalSize provides size,
) { Content() }
@@ -507,14 +532,24 @@
Log.e(GlanceAppWidgetTag, "Error in Glance App Widget", throwable)
}
-private fun Intent.extractAppWidgetIds() =
- getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS)
- ?: intArrayOf(
- getIntExtra(
- AppWidgetManager.EXTRA_APPWIDGET_ID,
- AppWidgetManager.INVALID_APPWIDGET_ID
- ).also {
- check(it != AppWidgetManager.INVALID_APPWIDGET_ID) {
- "Cannot determine the app widget id"
- }
- })
+/** Update all App Widgets managed by the [GlanceAppWidget] class. */
+public suspend fun GlanceAppWidget.updateAll(@Suppress("ContextFirst") context: Context) {
+ val manager = GlanceAppWidgetManager(context)
+ manager.getGlanceIds(javaClass).forEach { update(context, it) }
+}
+
+/**
+ * Update all App Widgets managed by the [GlanceAppWidget] class, if they fulfill some condition.
+ */
+public suspend inline fun <reified State> GlanceAppWidget.updateIf(
+ @Suppress("ContextFirst") context: Context,
+ predicate: (State) -> Boolean
+) {
+ val stateDef = stateDefinition
+ requireNotNull(stateDef) { "GlanceAppWidget.updateIf cannot be used if no state is defined." }
+ val manager = GlanceAppWidgetManager(context)
+ manager.getGlanceIds(javaClass).forEach { glanceId ->
+ val state = getAppWidgetState(context, stateDef, glanceId) as State
+ if (predicate(state)) update(context, glanceId)
+ }
+}
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt
index 6545359..a276ec9 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidgetReceiver.kt
@@ -43,6 +43,10 @@
*
* Note: If you override any of the [AppWidgetProvider] methods, ensure you call their super-class
* implementation.
+ *
+ * Important: if you override any of the methods of this class, you must call the super
+ * implementation, and you must not call [AppWidgetProvider.goAsync], as it will be called by the
+ * super implementation. This means your processing time must be short.
*/
abstract class GlanceAppWidgetReceiver : AppWidgetProvider() {
@@ -88,10 +92,11 @@
}
}
- override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
- // TODO: When a widget is deleted, delete the datastore
- appWidgetIds?.forEach {
- createUniqueRemoteUiName(it)
+ @CallSuper
+ override fun onDeleted(context: Context, appWidgetIds: IntArray) {
+ goAsync {
+ updateManager(context)
+ appWidgetIds.forEach { glanceAppWidget.deleted(context, it) }
}
}
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterCallbackTrampolineActivity.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterInvisibleTrampolineActivity.kt
similarity index 60%
rename from glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterCallbackTrampolineActivity.kt
rename to glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterInvisibleTrampolineActivity.kt
index 96108e7..1cc17f8 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterCallbackTrampolineActivity.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterInvisibleTrampolineActivity.kt
@@ -17,28 +17,17 @@
package androidx.glance.appwidget
import android.app.Activity
-import android.content.Intent
import android.os.Bundle
/**
- * Trampoline activity for handling click interactions of list adapter items that invoke action
- * callbacks.
+ * Trampoline activity for handling click interactions of list adapter items that invoke actions
+ * that don't launch an activity. Thus not showing any UI.
*/
@Suppress("ForbiddenSuperClass")
-internal class ListAdapterCallbackTrampolineActivity : Activity() {
+internal class ListAdapterInvisibleTrampolineActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- intent?.getParcelableExtra<Intent>(ActionIntentKey)
- ?.let { sendBroadcast(it) }
- ?: error("List adapter activity trampoline invoked without specifying target intent.")
- finish()
+ launchTrampolineAction(intent)
}
-
- companion object {
- private const val ActionIntentKey = "UPDATE_CONTENT_ACTION_INTENT"
-
- internal fun Intent.putBroadcastIntentExtra(actionIntent: Intent) =
- putExtra(ActionIntentKey, actionIntent)
- }
-}
+}
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterTrampoline.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterTrampoline.kt
new file mode 100644
index 0000000..5ff4bc4
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterTrampoline.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.appwidget
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Build
+import android.widget.RemoteViews
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+
+internal enum class ListAdapterTrampolineType {
+ ACTIVITY, BROADCAST, SERVICE, FOREGROUND_SERVICE
+}
+
+/**
+ * Wraps the "action intent" into an activity trampoline intent, where it will be invoked based on
+ * the type, with modifying its content.
+ *
+ * @see launchTrampolineAction
+ */
+internal fun Intent.applyTrampolineIntent(
+ context: Context,
+ viewId: Int,
+ type: ListAdapterTrampolineType,
+): Intent {
+ val target = if (type == ListAdapterTrampolineType.ACTIVITY) {
+ ListAdapterTrampolineActivity::class.java
+ } else {
+ ListAdapterInvisibleTrampolineActivity::class.java
+ }
+ return Intent(context, target).apply {
+ data = Uri.parse(toUri(0))
+ .buildUpon()
+ .scheme(type.name)
+ .path(viewId.toString())
+ .build()
+ putExtra(ActionTypeKey, type.name)
+ putExtra(ActionIntentKey, this@applyTrampolineIntent)
+ }
+}
+
+/**
+ * Unwraps and launches the action intent based on its type.
+ *
+ * @see applyTrampolineIntent
+ */
+internal fun Activity.launchTrampolineAction(intent: Intent) {
+ val actionIntent = requireNotNull(intent.getParcelableExtra<Intent>(ActionIntentKey)) {
+ "List adapter activity trampoline invoked without specifying target intent."
+ }
+ if (intent.hasExtra(RemoteViews.EXTRA_CHECKED)) {
+ actionIntent.putExtra(
+ RemoteViews.EXTRA_CHECKED,
+ intent.getBooleanExtra(RemoteViews.EXTRA_CHECKED, false)
+ )
+ }
+ val type = requireNotNull(intent.getStringExtra(ActionTypeKey)) {
+ "List adapter activity trampoline invoked without trampoline type"
+ }
+ when (ListAdapterTrampolineType.valueOf(type)) {
+ ListAdapterTrampolineType.ACTIVITY -> startActivity(actionIntent)
+ ListAdapterTrampolineType.BROADCAST -> sendBroadcast(actionIntent)
+ ListAdapterTrampolineType.SERVICE -> startService(actionIntent)
+ ListAdapterTrampolineType.FOREGROUND_SERVICE -> {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ ListAdapterTrampolineApi26Impl.startForegroundService(
+ context = this,
+ intent = actionIntent
+ )
+ } else {
+ startService(actionIntent)
+ }
+ }
+ }
+ finish()
+}
+
+private const val ActionTypeKey = "ACTION_TYPE"
+private const val ActionIntentKey = "ACTION_INTENT"
+
+@RequiresApi(Build.VERSION_CODES.O)
+private object ListAdapterTrampolineApi26Impl {
+ @DoNotInline
+ fun startForegroundService(context: Context, intent: Intent) {
+ context.startForegroundService(intent)
+ }
+}
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterLaunchActivityTrampolineActivity.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterTrampolineActivity.kt
similarity index 60%
rename from glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterLaunchActivityTrampolineActivity.kt
rename to glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterTrampolineActivity.kt
index 38b17c7..4fdbdc9 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterLaunchActivityTrampolineActivity.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/ListAdapterTrampolineActivity.kt
@@ -17,7 +17,6 @@
package androidx.glance.appwidget
import android.app.Activity
-import android.content.Intent
import android.os.Bundle
/**
@@ -25,24 +24,10 @@
* This trampoline is only used for device versions before [android.os.Build.VERSION_CODES.Q].
*/
@Suppress("ForbiddenSuperClass")
-internal class ListAdapterLaunchActivityTrampolineActivity : Activity() {
+internal class ListAdapterTrampolineActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- intent?.getParcelableExtra<Intent>(ActionIntentKey)
- ?.let {
- // Copy flags from trampoline intent to action intent
- it.flags = it.flags or intent.flags
- startActivity(it)
- }
- ?: error("List adapter activity trampoline invoked without specifying target intent.")
- finish()
- }
-
- companion object {
- private const val ActionIntentKey = "ACTIVITY_ACTION_INTENT"
-
- internal fun Intent.putActivityIntentExtra(actionIntent: Intent) =
- putExtra(ActionIntentKey, actionIntent)
+ launchTrampolineAction(intent)
}
}
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/Switch.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/Switch.kt
index b6f2a50..a20a4e5 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/Switch.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/Switch.kt
@@ -21,6 +21,9 @@
import androidx.glance.Emittable
import androidx.glance.GlanceModifier
import androidx.glance.GlanceNode
+import androidx.glance.action.Action
+import androidx.glance.action.ActionModifier
+import androidx.glance.appwidget.action.CompoundButtonAction
import androidx.glance.appwidget.unit.CheckableColorProvider
import androidx.glance.appwidget.unit.CheckedUncheckedColorProvider.Companion.createCheckableColorProvider
import androidx.glance.appwidget.unit.ResourceCheckableColorProvider
@@ -111,6 +114,10 @@
* Adds a switch view to the glance view.
*
* @param checked whether the switch is checked
+ * @param onCheckedChange the action to be run when the switch is clicked. The current value of
+ * checked is provided to this action in its ActionParameters, and can be retrieved using the
+ * [ToggleableStateKey]. If this action launches an activity, the current value of checked will be
+ * passed as an intent extra with the name [RemoteViews.EXTRA_CHECKED].
* @param modifier the modifier to apply to the switch
* @param text the text to display to the end of the switch
* @param style the style to apply to [text]
@@ -119,17 +126,23 @@
@Composable
fun Switch(
checked: Boolean,
+ onCheckedChange: Action?,
modifier: GlanceModifier = GlanceModifier,
text: String = "",
style: TextStyle? = null,
colors: SwitchColors = SwitchDefaults.colors
) {
+ val finalModifier = if (onCheckedChange != null) {
+ modifier.then(ActionModifier(CompoundButtonAction(onCheckedChange, checked)))
+ } else {
+ modifier
+ }
GlanceNode(
factory = ::EmittableSwitch,
update = {
this.set(checked) { this.checked = it }
this.set(text) { this.text = it }
- this.set(modifier) { this.modifier = it }
+ this.set(finalModifier) { this.modifier = it }
this.set(style) { this.style = it }
this.set(colors) { this.colors = it }
}
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/WidgetLayout.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/WidgetLayout.kt
index e3a38e6..71785ab 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/WidgetLayout.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/WidgetLayout.kt
@@ -280,11 +280,13 @@
override fun getLocation(context: Context, fileKey: String): File =
context.dataStoreFile(fileKey)
- @Suppress("UNCHECKED_CAST")
- override suspend fun <T> getDataStore(context: Context, fileKey: String): DataStore<T> =
+ override suspend fun getDataStore(
+ context: Context,
+ fileKey: String,
+ ): DataStore<LayoutProto.LayoutConfig> =
DataStoreFactory.create(serializer = LayoutProtoSerializer) {
context.dataStoreFile(fileKey)
- } as DataStore<T>
+ }
}
private fun Alignment.Vertical.toProto() = when (this) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/CompoundButtonAction.kt
similarity index 68%
copy from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt
copy to glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/CompoundButtonAction.kt
index d721bff..cdc9db0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/CompoundButtonAction.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.glance.appwidget.action
-import androidx.compose.foundation.lazy.layout.LazyLayoutItemsProvider
+import androidx.glance.action.Action
-internal interface LazyListItemsProvider : LazyLayoutItemsProvider {
- /** The list of indexes of the sticky header items */
- val headerIndexes: List<Int>
-}
+internal class CompoundButtonAction(
+ val innerAction: Action,
+ val checked: Boolean
+) : Action
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LaunchActivityIntentAction.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LaunchActivityIntentAction.kt
index 5ce93e9..7e7f48a 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LaunchActivityIntentAction.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LaunchActivityIntentAction.kt
@@ -19,9 +19,14 @@
import android.app.Activity
import android.content.Intent
import androidx.glance.action.Action
+import androidx.glance.action.ActionParameters
import androidx.glance.action.LaunchActivityAction
+import androidx.glance.action.actionParametersOf
-internal class LaunchActivityIntentAction(val intent: Intent) : LaunchActivityAction
+internal class LaunchActivityIntentAction(
+ val intent: Intent,
+ override val parameters: ActionParameters = actionParametersOf()
+) : LaunchActivityAction
/**
* Creates an [Action] that launches an [Activity] from the given [Intent] when triggered. The
@@ -30,5 +35,10 @@
* This action is supported by app widgets only.
*
* @param intent the intent used to launch the activity
+ * @param parameters the parameters associated with the action. Parameter values will be added to
+ * the activity intent, keyed by the parameter key name string.
*/
-public fun actionLaunchActivity(intent: Intent): Action = LaunchActivityIntentAction(intent)
+public fun actionLaunchActivity(
+ intent: Intent,
+ parameters: ActionParameters = actionParametersOf()
+): Action = LaunchActivityIntentAction(intent, parameters)
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LaunchBroadcastAction.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LaunchBroadcastAction.kt
new file mode 100644
index 0000000..c4a050f
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LaunchBroadcastAction.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.appwidget.action
+
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Intent
+import androidx.glance.action.Action
+
+internal sealed interface LaunchBroadcastReceiverAction : Action
+
+internal class LaunchBroadcastReceiverActionAction(
+ val action: String,
+ val componentName: ComponentName? = null,
+) : LaunchBroadcastReceiverAction
+
+internal class LaunchBroadcastReceiverComponentAction(
+ val componentName: ComponentName,
+) : LaunchBroadcastReceiverAction
+
+internal class LaunchBroadcastReceiverClassAction(
+ val receiverClass: Class<out BroadcastReceiver>,
+) : LaunchBroadcastReceiverAction
+
+internal class LaunchBroadcastReceiverIntentAction(
+ val intent: Intent,
+) : LaunchBroadcastReceiverAction
+
+/**
+ * Creates an [Action] that launches the [BroadcastReceiver] specified by the given action.
+ *
+ * @param action of the BroadcastReceiver to launch
+ * @param componentName optional [ComponentName] of the target BroadcastReceiver
+ */
+public fun actionLaunchBroadcastReceiver(
+ action: String,
+ componentName: ComponentName? = null
+): Action = LaunchBroadcastReceiverActionAction(action, componentName)
+
+/**
+ * Creates an [Action] that launches a [BroadcastReceiver] from the given [Intent] when triggered.
+ * The intent should specify a component with [Intent.setClass] or [Intent.setComponent].
+ *
+ * @param intent the [Intent] used to launch the [BroadcastReceiver]
+ */
+public fun actionLaunchBroadcastReceiver(intent: Intent): Action =
+ LaunchBroadcastReceiverIntentAction(intent)
+
+/**
+ * Creates an [Action] that launches the [BroadcastReceiver] specified by the given [ComponentName].
+ *
+ * @param componentName component of the [BroadcastReceiver] to launch
+ */
+public fun actionLaunchBroadcastReceiver(componentName: ComponentName): Action =
+ LaunchBroadcastReceiverComponentAction(componentName)
+
+/**
+ * Creates an [Action] that launches the specified [BroadcastReceiver] when triggered.
+ *
+ * @param receiver class of the [BroadcastReceiver] to launch
+ */
+public fun <T : BroadcastReceiver> actionLaunchBroadcastReceiver(receiver: Class<T>): Action =
+ LaunchBroadcastReceiverClassAction(receiver)
+
+/**
+ * Creates an [Action] that launches the specified [BroadcastReceiver] when triggered.
+ */
+@Suppress("MissingNullability") // Shouldn't need to specify @NonNull. b/199284086
+public inline fun <reified T : BroadcastReceiver> actionLaunchBroadcastReceiver(): Action =
+ actionLaunchBroadcastReceiver(T::class.java)
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LaunchServiceAction.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LaunchServiceAction.kt
new file mode 100644
index 0000000..183e9bf
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/LaunchServiceAction.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.appwidget.action
+
+import android.app.Service
+import android.content.ComponentName
+import android.content.Intent
+import androidx.glance.action.Action
+
+internal sealed interface LaunchServiceAction : Action {
+ val isForegroundService: Boolean
+}
+
+internal class LaunchServiceComponentAction(
+ val componentName: ComponentName,
+ override val isForegroundService: Boolean
+) : LaunchServiceAction
+
+internal class LaunchServiceClassAction(
+ val serviceClass: Class<out Service>,
+ override val isForegroundService: Boolean
+) : LaunchServiceAction
+
+internal class LaunchServiceIntentAction(
+ val intent: Intent,
+ override val isForegroundService: Boolean
+) : LaunchServiceAction
+
+/**
+ * Creates an [Action] that launches a [Service] from the given [Intent] when triggered. The
+ * intent should specify a component with [Intent.setClass] or [Intent.setComponent].
+ *
+ * @param intent the intent used to launch the activity
+ * @param isForegroundService set to true when the provided [Service] runs in foreground. This flag
+ * is only used for device versions after [android.os.Build.VERSION_CODES.O] that requires
+ * foreground service to be launched differently
+ */
+public fun actionLaunchService(intent: Intent, isForegroundService: Boolean = false): Action =
+ LaunchServiceIntentAction(intent, isForegroundService)
+
+/**
+ * Creates an [Action] that launches the [Service] specified by the given [ComponentName].
+ *
+ * @param componentName component of the Service to launch
+ * @param isForegroundService set to true when the provided [Service] runs in foreground. This flag
+ * is only used for device versions after [android.os.Build.VERSION_CODES.O] that requires
+ * foreground service to be launched differently
+ */
+public fun actionLaunchService(
+ componentName: ComponentName,
+ isForegroundService: Boolean = false
+): Action = LaunchServiceComponentAction(componentName, isForegroundService)
+
+/**
+ * Creates an [Action] that launches the specified [Service] when triggered.
+ *
+ * @param service class of the Service to launch
+ * @param isForegroundService set to true when the provided [Service] runs in foreground. This flag
+ * is only used for device versions after [android.os.Build.VERSION_CODES.O] that requires
+ * foreground service to be launched differently
+ */
+public fun <T : Service> actionLaunchService(
+ service: Class<T>,
+ isForegroundService: Boolean = false
+): Action =
+ LaunchServiceClassAction(service, isForegroundService)
+
+/**
+ * Creates an [Action] that launches the specified [Service] when triggered.
+ *
+ * @param isForegroundService set to true when the provided [Service] runs in foreground. This flag
+ * is only used for device versions after [android.os.Build.VERSION_CODES.O] that requires
+ * foreground service to be launched differently.
+ */
+@Suppress("MissingNullability") /* Shouldn't need to specify @NonNull. b/199284086 */
+public inline fun <reified T : Service> actionLaunchService(
+ isForegroundService: Boolean = false
+): Action = actionLaunchService(T::class.java, isForegroundService)
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/RunCallbackAction.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/RunCallbackAction.kt
index 8eb48e5..43b3ef2 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/RunCallbackAction.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/RunCallbackAction.kt
@@ -77,12 +77,12 @@
parameters: ActionParameters = actionParametersOf()
): Action = RunCallbackAction(callbackClass, parameters)
-@Suppress("MissingNullability") /* Shouldn't need to specify @NonNull. b/199284086 */
/**
* Creates an [Action] that executes a given [ActionCallback] implementation
*
* @param parameters the parameters associated with the action
*/
+@Suppress("MissingNullability") // Shouldn't need to specify @NonNull. b/199284086
public inline fun <reified T : ActionCallback> actionRunCallback(
parameters: ActionParameters = actionParametersOf()
): Action = actionRunCallback(T::class.java, parameters)
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/Toggleable.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/Toggleable.kt
new file mode 100644
index 0000000..f47f573e
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/action/Toggleable.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.appwidget.action
+
+import android.widget.RemoteViews
+import androidx.glance.action.ActionParameters
+
+/**
+ * This key is used to access the current checked value for actions that are triggered by compound
+ * button interactions (e.g. [CheckBox], [Switch]). This key is used to retrieve the value from the
+ * [ActionParameters] passed to the action.
+ */
+val ToggleableStateKey = ActionParameters.Key<Boolean>(RemoteViews.EXTRA_CHECKED)
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/state/GlanceAppWidgetState.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/state/GlanceAppWidgetState.kt
new file mode 100644
index 0000000..3c4455d
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/state/GlanceAppWidgetState.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.appwidget.state
+
+import android.content.Context
+import androidx.glance.GlanceId
+import androidx.glance.appwidget.AppWidgetId
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.createUniqueRemoteUiName
+import androidx.glance.state.GlanceState
+import androidx.glance.state.GlanceStateDefinition
+
+/**
+ * Retrieve the state of an app widget.
+ *
+ * The state definition must be the one used for that particular app widget.
+ */
+public suspend fun <T> getAppWidgetState(
+ context: Context,
+ definition: GlanceStateDefinition<T>,
+ glanceId: GlanceId,
+): T {
+ require(glanceId is AppWidgetId) { "The glance ID is not the one of an App Widget" }
+ return GlanceState.getValue(context, definition, createUniqueRemoteUiName(glanceId.appWidgetId))
+}
+
+/**
+ * Update the state of an app widget.
+ *
+ * The state definition must be the one used for that particular app widget.
+ */
+public suspend fun <T> updateAppWidgetState(
+ context: Context,
+ definition: GlanceStateDefinition<T>,
+ glanceId: GlanceId,
+ updateState: suspend (T) -> T,
+): T {
+ require(glanceId is AppWidgetId) { "The glance ID is not the one of an App Widget" }
+ return GlanceState.updateValue(
+ context,
+ definition,
+ createUniqueRemoteUiName(glanceId.appWidgetId),
+ updateState,
+ )
+}
+
+/** Get the state of an App Widget. */
+@Suppress("UNCHECKED_CAST")
+public suspend fun <T> GlanceAppWidget.getAppWidgetState(
+ @Suppress("ContextFirst") context: Context,
+ glanceId: GlanceId
+): T =
+ getAppWidgetState(
+ context,
+ checkNotNull(stateDefinition) { "No state defined in this provider" },
+ glanceId
+ ) as T
+
+/** Update the state of an app widget. */
+@Suppress("UNCHECKED_CAST")
+public suspend fun <T> GlanceAppWidget.updateAppWidgetState(
+ @Suppress("ContextFirst") context: Context,
+ glanceId: GlanceId,
+ updateState: suspend (T) -> T,
+): T =
+ updateAppWidgetState(
+ context,
+ checkNotNull(stateDefinition as GlanceStateDefinition<T>) {
+ "No state defined in this provider"
+ },
+ glanceId,
+ updateState,
+ )
diff --git a/glance/glance-appwidget/src/androidMain/res/color/switch_off_ambient_shadow.xml b/glance/glance-appwidget/src/androidMain/res/color/switch_off_ambient_shadow.xml
new file mode 100644
index 0000000..e31f49e
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/color/switch_off_ambient_shadow.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:centerX="13"
+ android:centerY="12"
+ android:gradientRadius="10.25"
+ android:type="radial">
+ <item
+ android:color="#6D050505"
+ android:offset="0" />
+ <item
+ android:color="#50050505"
+ android:offset="0.6" />
+ <item
+ android:color="#20000000"
+ android:offset="0.90" />
+ <item
+ android:color="#00000000"
+ android:offset="1" />
+</gradient>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/color/switch_off_key_shadow.xml b/glance/glance-appwidget/src/androidMain/res/color/switch_off_key_shadow.xml
new file mode 100644
index 0000000..2834f7f
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/color/switch_off_key_shadow.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:centerX="13"
+ android:centerY="14"
+ android:gradientRadius="10"
+ android:type="radial">
+ <item
+ android:color="#6D050505"
+ android:offset="0" />
+ <item
+ android:color="#50050505"
+ android:offset="0.6" />
+ <item
+ android:color="#20000000"
+ android:offset="0.8" />
+ <item
+ android:color="#00000000"
+ android:offset="1" />
+</gradient>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/color/switch_on_ambient_shadow.xml b/glance/glance-appwidget/src/androidMain/res/color/switch_on_ambient_shadow.xml
new file mode 100644
index 0000000..3f4f9cc
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/color/switch_on_ambient_shadow.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:centerX="24"
+ android:centerY="12"
+ android:gradientRadius="10.25"
+ android:type="radial">
+ <item
+ android:color="#6D050505"
+ android:offset="0" />
+ <item
+ android:color="#50050505"
+ android:offset="0.6" />
+ <item
+ android:color="#20000000"
+ android:offset="0.90" />
+ <item
+ android:color="#00000000"
+ android:offset="1" />
+</gradient>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/color/switch_on_key_shadow.xml b/glance/glance-appwidget/src/androidMain/res/color/switch_on_key_shadow.xml
new file mode 100644
index 0000000..d374869
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/color/switch_on_key_shadow.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:centerX="24"
+ android:centerY="14"
+ android:gradientRadius="10.25"
+ android:type="radial">
+ <item
+ android:color="#6D050505"
+ android:offset="0" />
+ <item
+ android:color="#50050505"
+ android:offset="0.6" />
+ <item
+ android:color="#40000000"
+ android:offset="0.8" />
+ <item
+ android:color="#00000000"
+ android:offset="1" />
+</gradient>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/glance/glance-appwidget/src/androidMain/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00001.9.png
deleted file mode 100644
index 4657a25..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ /dev/null
Binary files differ
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/glance/glance-appwidget/src/androidMain/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00012.9.png
deleted file mode 100644
index 8dc415b..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-hdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ /dev/null
Binary files differ
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_unchecked.xml b/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_unchecked.xml
deleted file mode 100644
index d2d35b7..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_unchecked.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/abc_btn_switch_to_on_mtrl_00001"
- android:insetLeft="18dp"/>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/glance/glance-appwidget/src/androidMain/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00001.9.png
deleted file mode 100644
index d0a41a5..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ /dev/null
Binary files differ
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/glance/glance-appwidget/src/androidMain/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00012.9.png
deleted file mode 100644
index bebb1e2..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-mdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ /dev/null
Binary files differ
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/glance/glance-appwidget/src/androidMain/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
deleted file mode 100644
index 1d29f9a..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ /dev/null
Binary files differ
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/glance/glance-appwidget/src/androidMain/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
deleted file mode 100644
index 92b43ba..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-xhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ /dev/null
Binary files differ
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/glance/glance-appwidget/src/androidMain/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
deleted file mode 100644
index c079867..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ /dev/null
Binary files differ
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/glance/glance-appwidget/src/androidMain/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
deleted file mode 100644
index 3b9dc7c..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-xxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ /dev/null
Binary files differ
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png b/glance/glance-appwidget/src/androidMain/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
deleted file mode 100644
index 639e6cb..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00001.9.png
+++ /dev/null
Binary files differ
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png b/glance/glance-appwidget/src/androidMain/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
deleted file mode 100644
index 355d5b7..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable-xxxhdpi/abc_btn_switch_to_on_mtrl_00012.9.png
+++ /dev/null
Binary files differ
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_animated.xml b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_animated.xml
new file mode 100644
index 0000000..c3491b6
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_animated.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/on"
+ android:drawable="@drawable/switch_thumb_on"
+ android:state_enabled="true" />
+ <item
+ android:id="@+id/off"
+ android:drawable="@drawable/switch_thumb_off" />
+ <transition
+ android:drawable="@drawable/switch_thumb_off_to_on"
+ android:fromId="@+id/off"
+ android:toId="@+id/on" />
+ <transition
+ android:drawable="@drawable/switch_thumb_on_to_off"
+ android:fromId="@+id/on"
+ android:toId="@+id/off" />
+</animated-selector>
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_material.xml b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_material.xml
deleted file mode 100644
index 939b1c2..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_material.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true" android:drawable="@drawable/switch_thumb_material_checked" />
- <item android:drawable="@drawable/switch_thumb_material_unchecked" />
-</selector>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_material_checked.xml b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_material_checked.xml
deleted file mode 100644
index ece7ee7..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_material_checked.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/abc_btn_switch_to_on_mtrl_00012"
- android:insetLeft="18dp"/>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_material_unchecked.xml b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_material_unchecked.xml
deleted file mode 100644
index a151ef9..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_material_unchecked.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- Copyright 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/abc_btn_switch_to_on_mtrl_00001"
- android:insetRight="18dp"/>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_off.xml b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_off.xml
new file mode 100644
index 0000000..721590f
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_off.xml
@@ -0,0 +1,25 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="40dp"
+ android:height="26dp"
+ android:viewportWidth="40"
+ android:viewportHeight="26"
+>
+ <group android:name="thumb" android:translateX="0">
+ <path
+ android:pathData="M13,22.5C18.7989,22.5 23.5,17.7989 23.5,12 23.5,6.201 18.7989,1.5 13,1.5 7.201,1.5 2.5,6.201 2.5,12 2.5,17.7989 7.201,22.5 13,22.5Z"
+ android:strokeWidth="1.05"
+ android:fillColor="@color/switch_off_ambient_shadow"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="m12.9681,24.2511c5.7734,0 10.4537,-5.084 10.4537,-11.3552 0,-6.2713 -4.6803,-11.3552 -10.4537,-11.3552 -5.7734,0 -10.4537,5.0839 -10.4537,11.3552 0,6.2713 4.6803,11.3552 10.4537,11.3552z"
+ android:strokeWidth="1.08951"
+ android:fillColor="@color/switch_off_key_shadow"
+ android:fillType="evenOdd"/>
+ <path
+ android:name="thumb_icon"
+ android:pathData="M13,22C18.5228,22 23,17.5228 23,12 23,6.4771 18.5228,2 13,2 7.4771,2 3,6.4771 3,12c0,5.5228 4.4771,10 10,10z"
+ android:strokeWidth="1"
+ android:fillColor="#fff"
+ android:fillType="evenOdd"/>
+ </group>
+</vector>
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_off_to_on.xml b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_off_to_on.xml
new file mode 100644
index 0000000..a162697
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_off_to_on.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:drawable="@drawable/switch_thumb_off">
+ <target
+ android:name="thumb" >
+ <aapt:attr name="android:animation">
+ <objectAnimator
+ android:propertyName="translateX"
+ android:valueFrom="0"
+ android:valueTo="14"
+ android:duration="250"
+ android:interpolator="@android:interpolator/linear" />
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_on.xml b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_on.xml
new file mode 100644
index 0000000..bf6ea1c
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_on.xml
@@ -0,0 +1,26 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="40dp"
+ android:height="26dp"
+ android:viewportWidth="40"
+ android:viewportHeight="26"
+ >
+ <group
+ android:name="thumb"
+ android:translateX="3">
+ <path
+ android:fillColor="@color/switch_on_key_shadow"
+ android:fillType="evenOdd"
+ android:pathData="m23.9373,24.5329c5.7734,0 10.4537,-5.084 10.4537,-11.3552 0,-6.2713 -4.6803,-11.3552 -10.4537,-11.3552 -5.7734,0 -10.4537,5.0839 -10.4537,11.3552 0,6.2713 4.6803,11.3552 10.4537,11.3552z"
+ android:strokeWidth="1.08951" />
+ <path
+ android:fillColor="@color/switch_on_ambient_shadow"
+ android:fillType="evenOdd"
+ android:pathData="M24,22.5C29.7989,22.5 34.5,17.7989 34.5,12 34.5,6.201 29.7989,1.5 24,1.5 18.201,1.5 13.5,6.201 13.5,12c0,5.7989 4.701,10.5 10.5,10.5z"
+ android:strokeWidth="1.05" />
+ <path
+ android:name="thumb_icon"
+ android:fillColor="#fff"
+ android:fillType="evenOdd"
+ android:pathData="M24,22C29.5228,22 34,17.5228 34,12C34,6.4771 29.5228,2 24,2C18.4772,2 14,6.4771 14,12C14,17.5228 18.4772,22 24,22Z" />
+ </group>
+</vector>
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_on_to_off.xml b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_on_to_off.xml
new file mode 100644
index 0000000..e78f0c2
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/drawable/switch_thumb_on_to_off.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:drawable="@drawable/switch_thumb_on">
+ <target
+ android:name="thumb" >
+ <aapt:attr name="android:animation">
+ <objectAnimator
+ android:propertyName="translateX"
+ android:valueFrom="0"
+ android:valueTo="-14"
+ android:duration="250"
+ android:interpolator="@android:interpolator/linear" />
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/switch_track.xml b/glance/glance-appwidget/src/androidMain/res/drawable/switch_track.xml
new file mode 100644
index 0000000..3c0f448
--- /dev/null
+++ b/glance/glance-appwidget/src/androidMain/res/drawable/switch_track.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="40dp"
+ android:height="26dp"
+ android:viewportWidth="40"
+ android:viewportHeight="26"
+>
+ <path
+ android:name="track"
+ android:pathData="M10,5L30,5A7,7 0,0 1,37 12L37,12A7,7 0,0 1,30 19L10,19A7,7 0,0 1,3 12L3,12A7,7 0,0 1,10 5z"
+ android:fillColor="@android:color/white"
+ android:fillAlpha="?android:attr/disabledAlpha"/>
+</vector>
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable/switch_track_material.xml b/glance/glance-appwidget/src/androidMain/res/drawable/switch_track_material.xml
deleted file mode 100644
index 0e93096..0000000
--- a/glance/glance-appwidget/src/androidMain/res/drawable/switch_track_material.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:gravity="center_vertical|fill_horizontal"
- android:start="4dp"
- android:end="4dp">
- <shape android:shape="rectangle">
- <corners android:radius="7dp" />
- <solid android:color="@color/white_disabled_material_anim" />
- <size android:height="14dp" />
- </shape>
- </item>
-</layer-list>
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/androidMain/res/layout/swtch_text.xml b/glance/glance-appwidget/src/androidMain/res/layout/switch_text.xml
similarity index 100%
rename from glance/glance-appwidget/src/androidMain/res/layout/swtch_text.xml
rename to glance/glance-appwidget/src/androidMain/res/layout/switch_text.xml
diff --git a/glance/glance-appwidget/src/androidMain/res/layout/swtch_thumb.xml b/glance/glance-appwidget/src/androidMain/res/layout/switch_thumb.xml
similarity index 94%
rename from glance/glance-appwidget/src/androidMain/res/layout/swtch_thumb.xml
rename to glance/glance-appwidget/src/androidMain/res/layout/switch_thumb.xml
index bb31034..8106726 100644
--- a/glance/glance-appwidget/src/androidMain/res/layout/swtch_thumb.xml
+++ b/glance/glance-appwidget/src/androidMain/res/layout/switch_thumb.xml
@@ -18,5 +18,5 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
- android:src="@drawable/switch_thumb_material"
+ android:src="@drawable/switch_thumb_animated"
style="@style/Glance.AppWidget.SwitchThumb"/>
diff --git a/glance/glance-appwidget/src/androidMain/res/layout/swtch_track.xml b/glance/glance-appwidget/src/androidMain/res/layout/switch_track.xml
similarity index 94%
rename from glance/glance-appwidget/src/androidMain/res/layout/swtch_track.xml
rename to glance/glance-appwidget/src/androidMain/res/layout/switch_track.xml
index 375967e..e65bc84 100644
--- a/glance/glance-appwidget/src/androidMain/res/layout/swtch_track.xml
+++ b/glance/glance-appwidget/src/androidMain/res/layout/switch_track.xml
@@ -17,5 +17,5 @@
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="42dp"
android:layout_height="28dp"
- android:src="@drawable/switch_track_material"
+ android:src="@drawable/switch_track"
style="@style/Glance.AppWidget.SwitchTrack"/>
diff --git a/glance/glance-appwidget/src/androidMain/res/layout/swtch_backport.xml b/glance/glance-appwidget/src/androidMain/res/layout/swtch_backport.xml
index 0258870..f6caf8a 100644
--- a/glance/glance-appwidget/src/androidMain/res/layout/swtch_backport.xml
+++ b/glance/glance-appwidget/src/androidMain/res/layout/swtch_backport.xml
@@ -25,7 +25,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:layout="@layout/swtch_text" />
+ android:layout="@layout/switch_text" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -35,12 +35,12 @@
android:id="@id/switchTrack"
android:layout_width="42dp"
android:layout_height="28dp"
- android:layout="@layout/swtch_track" />
+ android:layout="@layout/switch_track" />
<ViewStub
android:id="@id/switchThumb"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout="@layout/swtch_thumb" />
+ android:layout_width="42dp"
+ android:layout_height="28dp"
+ android:layout="@layout/switch_thumb" />
</FrameLayout>
</LinearLayout>
diff --git a/glance/glance-appwidget/src/androidMain/res/values/ids.xml b/glance/glance-appwidget/src/androidMain/res/values/ids.xml
index 6ab0397..5d9dcb7 100644
--- a/glance/glance-appwidget/src/androidMain/res/values/ids.xml
+++ b/glance/glance-appwidget/src/androidMain/res/values/ids.xml
@@ -23,8 +23,8 @@
<id name="sizeView" />
<id name="sizeViewStub" />
<id name="switchText" />
- <id name="switchTrack" />
<id name="switchThumb" />
+ <id name="switchTrack" />
<id name="rootView" />
<id name="rootStubId" />
<id name="deletedViewId" />
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml b/glance/glance-appwidget/src/main/res/values-v29/themes.xml
similarity index 79%
copy from glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
copy to glance/glance-appwidget/src/main/res/values-v29/themes.xml
index 987c157..4cce739 100644
--- a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
+++ b/glance/glance-appwidget/src/main/res/values-v29/themes.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/abc_btn_switch_to_on_mtrl_00012"
- android:insetRight="18dp"/>
\ No newline at end of file
+<resources>
+ <style name="Glance.AppWidget.Theme" parent="android:Theme.DeviceDefault.DayNight" />
+</resources>
diff --git a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml b/glance/glance-appwidget/src/main/res/values/themes.xml
similarity index 79%
rename from glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
rename to glance/glance-appwidget/src/main/res/values/themes.xml
index 987c157..efd5acd 100644
--- a/glance/glance-appwidget/src/androidMain/res/drawable-ldrtl/switch_thumb_material_checked.xml
+++ b/glance/glance-appwidget/src/main/res/values/themes.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/abc_btn_switch_to_on_mtrl_00012"
- android:insetRight="18dp"/>
\ No newline at end of file
+<resources>
+ <style name="Glance.AppWidget.Theme" parent="android:Theme.DeviceDefault"/>
+</resources>
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/RemoteViewsTranslatorKtTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/RemoteViewsTranslatorKtTest.kt
index dcdf7cc..14e36af 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/RemoteViewsTranslatorKtTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/RemoteViewsTranslatorKtTest.kt
@@ -638,6 +638,7 @@
val rv = context.runAndTranslate {
CheckBox(
checked = false,
+ onCheckedChange = null,
text = "test",
style = TextStyle(
color = ColorProvider(Color.Red),
@@ -665,6 +666,7 @@
val rv = context.runAndTranslate {
CheckBox(
checked = true,
+ onCheckedChange = null,
text = "test checked",
style = TextStyle(textDecoration = TextDecoration.LineThrough),
)
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/WidgetLayoutTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/WidgetLayoutTest.kt
index baa4073..a154511 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/WidgetLayoutTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/WidgetLayoutTest.kt
@@ -62,7 +62,11 @@
fun testLayout() = fakeCoroutineScope.runBlockingTest {
val root = runTestingComposition {
Column(horizontalAlignment = Alignment.End) {
- CheckBox(checked = false, modifier = GlanceModifier.fillMaxSize())
+ CheckBox(
+ checked = false,
+ onCheckedChange = null,
+ modifier = GlanceModifier.fillMaxSize()
+ )
Button(text = "test", onClick = actionLaunchActivity<Activity>())
Image(
ImageProvider(R.drawable.oval),
@@ -102,7 +106,11 @@
val appId = 999
val root = runTestingComposition {
Column {
- CheckBox(checked = true, modifier = GlanceModifier.fillMaxSize())
+ CheckBox(
+ checked = true,
+onCheckedChange = null,
+ modifier = GlanceModifier.fillMaxSize()
+ )
Button(text = "test", onClick = actionLaunchActivity<Activity>())
}
}
@@ -110,6 +118,7 @@
Column {
CheckBox(
checked = true,
+ onCheckedChange = null,
modifier = GlanceModifier.wrapContentWidth().fillMaxHeight()
)
Button(text = "test", onClick = actionLaunchActivity<Activity>())
@@ -324,13 +333,21 @@
val appId = 787
val root = runTestingComposition {
Column {
- CheckBox(checked = true, modifier = GlanceModifier.fillMaxSize())
+ CheckBox(
+ checked = true,
+ onCheckedChange = null,
+ modifier = GlanceModifier.fillMaxSize()
+ )
Button(text = "test", onClick = actionLaunchActivity<Activity>())
}
}
val root2 = runTestingComposition {
Column {
- CheckBox(checked = false, modifier = GlanceModifier.fillMaxSize())
+ CheckBox(
+ checked = false,
+ onCheckedChange = null,
+ modifier = GlanceModifier.fillMaxSize()
+ )
Button(text = "testtesttest", onClick = actionLaunchActivity<Activity>())
}
}
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/action/LaunchBroadcastReceiverActionTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/action/LaunchBroadcastReceiverActionTest.kt
new file mode 100644
index 0000000..79215f4
--- /dev/null
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/action/LaunchBroadcastReceiverActionTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.appwidget.action
+
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import androidx.glance.GlanceModifier
+import androidx.glance.action.ActionModifier
+import androidx.glance.action.clickable
+import androidx.glance.findModifier
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import kotlin.test.assertIs
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricTestRunner::class)
+class LaunchBroadcastReceiverActionTest {
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+
+ @Test
+ fun testLaunchClass() {
+ val modifiers =
+ GlanceModifier.clickable(actionLaunchBroadcastReceiver<TestBroadcastReceiver>())
+ val modifier = checkNotNull(modifiers.findModifier<ActionModifier>())
+ val action = assertIs<LaunchBroadcastReceiverClassAction>(modifier.action)
+ assertThat(action.receiverClass).isEqualTo(TestBroadcastReceiver::class.java)
+ }
+
+ @Test
+ fun testLaunchAction() {
+ val intentActionString = "test_action"
+ val modifiers = GlanceModifier.clickable(actionLaunchBroadcastReceiver(intentActionString))
+ val modifier = checkNotNull(modifiers.findModifier<ActionModifier>())
+ val action = assertIs<LaunchBroadcastReceiverActionAction>(modifier.action)
+ assertThat(action.action).isEqualTo(intentActionString)
+ assertThat(action.componentName).isNull()
+ }
+
+ @Test
+ fun testLaunchActionWithComponentName() {
+ val intentActionString = "test_action"
+ val componentName = ComponentName(
+ "androidx.glance.appwidget.action",
+ "androidx.glance.appwidget.action.TestBroadcastReceiver"
+ )
+ val modifiers = GlanceModifier.clickable(
+ actionLaunchBroadcastReceiver(
+ intentActionString,
+ componentName
+ )
+ )
+ val modifier = checkNotNull(modifiers.findModifier<ActionModifier>())
+ val action = assertIs<LaunchBroadcastReceiverActionAction>(modifier.action)
+ assertThat(action.action).isEqualTo(intentActionString)
+ assertThat(action.componentName).isEqualTo(componentName)
+ }
+
+ @Test
+ fun testLaunchIntent() {
+ val intentActionString = "test_action"
+ val intent =
+ Intent(context, TestBroadcastReceiver::class.java).setAction(intentActionString)
+ val modifiers = GlanceModifier.clickable(actionLaunchBroadcastReceiver(intent))
+ val modifier = checkNotNull(modifiers.findModifier<ActionModifier>())
+ val action = assertIs<LaunchBroadcastReceiverIntentAction>(modifier.action)
+ assertThat(action.intent).isEqualTo(intent)
+ assertThat(action.intent.action).isEqualTo(intentActionString)
+ }
+
+ @Test
+ fun testLaunchComponent() {
+ val componentName = ComponentName(
+ "androidx.glance.appwidget.action",
+ "androidx.glance.appwidget.action.TestBroadcastReceiver"
+ )
+ val modifiers = GlanceModifier.clickable(actionLaunchBroadcastReceiver(componentName))
+ val modifier = checkNotNull(modifiers.findModifier<ActionModifier>())
+ val action = assertIs<LaunchBroadcastReceiverComponentAction>(modifier.action)
+ assertThat(action.componentName).isEqualTo(componentName)
+ }
+}
+
+class TestBroadcastReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ // Nothing
+ }
+}
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/action/LaunchServiceActionTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/action/LaunchServiceActionTest.kt
new file mode 100644
index 0000000..5dbffd7
--- /dev/null
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/action/LaunchServiceActionTest.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.appwidget.action
+
+import android.app.Service
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.IBinder
+import androidx.glance.GlanceModifier
+import androidx.glance.action.ActionModifier
+import androidx.glance.action.clickable
+import androidx.glance.findModifier
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import kotlin.test.assertIs
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricTestRunner::class)
+class LaunchServiceActionTest {
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+
+ @Test
+ fun testLaunchClass() {
+ val modifiers = GlanceModifier.clickable(actionLaunchService<TestService>())
+ val modifier = checkNotNull(modifiers.findModifier<ActionModifier>())
+ val action = assertIs<LaunchServiceClassAction>(modifier.action)
+ assertThat(action.serviceClass).isEqualTo(TestService::class.java)
+ assertThat(action.isForegroundService).isEqualTo(false)
+ }
+
+ @Test
+ fun testLaunchClassWithForeground() {
+ val modifiers = GlanceModifier.clickable(
+ actionLaunchService<TestService>(
+ isForegroundService = true
+ )
+ )
+ val modifier = checkNotNull(modifiers.findModifier<ActionModifier>())
+ val action = assertIs<LaunchServiceClassAction>(modifier.action)
+ assertThat(action.serviceClass).isEqualTo(TestService::class.java)
+ assertThat(action.isForegroundService).isEqualTo(true)
+ }
+
+ @Test
+ fun testLaunchIntent() {
+ val intentActionString = "test_action"
+ val intent = Intent(context, TestService::class.java).setAction(intentActionString)
+ val modifiers = GlanceModifier.clickable(actionLaunchService(intent))
+ val modifier = checkNotNull(modifiers.findModifier<ActionModifier>())
+ val action = assertIs<LaunchServiceIntentAction>(modifier.action)
+ assertThat(action.intent).isEqualTo(intent)
+ assertThat(action.intent.action).isEqualTo(intentActionString)
+ assertThat(action.isForegroundService).isEqualTo(false)
+ }
+
+ @Test
+ fun testLaunchComponent() {
+ val componentName = ComponentName(
+ "androidx.glance.appwidget.action",
+ "androidx.glance.appwidget.action.TestService"
+ )
+ val modifiers = GlanceModifier.clickable(actionLaunchService(componentName))
+ val modifier = checkNotNull(modifiers.findModifier<ActionModifier>())
+ val action = assertIs<LaunchServiceComponentAction>(modifier.action)
+ assertThat(action.componentName).isEqualTo(componentName)
+ assertThat(action.isForegroundService).isEqualTo(false)
+ }
+}
+
+class TestService : Service() {
+ override fun onBind(p0: Intent?): IBinder? = null
+}
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/CheckBoxTranslatorTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/CheckBoxTranslatorTest.kt
index 8ba6e81..b56d57b9b 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/CheckBoxTranslatorTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/CheckBoxTranslatorTest.kt
@@ -24,6 +24,8 @@
import androidx.glance.appwidget.CheckBox
import androidx.glance.appwidget.CheckBoxColors
import androidx.glance.appwidget.ImageViewSubject.Companion.assertThat
+import androidx.glance.appwidget.action.ActionCallback
+import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.appwidget.applyRemoteViews
import androidx.glance.appwidget.configurationContext
import androidx.glance.appwidget.findViewByType
@@ -31,6 +33,7 @@
import androidx.glance.appwidget.test.R
import androidx.glance.appwidget.unit.ColorProvider
import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
@@ -61,6 +64,7 @@
val rv = context.runAndTranslate {
CheckBox(
checked = false,
+ onCheckedChange = null,
text = "Check",
colors = CheckBoxColors(checkedColor = Color.Red, uncheckedColor = Color.Blue)
)
@@ -77,6 +81,7 @@
val rv = context.runAndTranslate {
CheckBox(
checked = true,
+ onCheckedChange = null,
text = "Check",
colors = CheckBoxColors(checkedColor = Color.Red, uncheckedColor = Color.Blue)
)
@@ -93,6 +98,7 @@
val rv = lightContext.runAndTranslate {
CheckBox(
checked = false,
+ onCheckedChange = null,
text = "Check",
colors = CheckBoxColors(
checkedColor = ColorProvider(day = Color.Red, night = Color.Blue),
@@ -112,6 +118,7 @@
val rv = darkContext.runAndTranslate {
CheckBox(
checked = false,
+ onCheckedChange = null,
text = "Check",
colors = CheckBoxColors(
checkedColor = ColorProvider(day = Color.Red, night = Color.Blue),
@@ -131,6 +138,7 @@
val rv = lightContext.runAndTranslate {
CheckBox(
checked = true,
+ onCheckedChange = null,
text = "Check",
colors = CheckBoxColors(
checkedColor = ColorProvider(day = Color.Red, night = Color.Blue),
@@ -150,6 +158,7 @@
val rv = darkContext.runAndTranslate {
CheckBox(
checked = true,
+ onCheckedChange = null,
text = "Check",
colors = CheckBoxColors(
checkedColor = ColorProvider(day = Color.Red, night = Color.Blue),
@@ -169,6 +178,7 @@
val rv = context.runAndTranslate {
CheckBox(
checked = true,
+ onCheckedChange = null,
text = "Check",
colors = CheckBoxColors(R.color.my_checkbox_colors)
)
@@ -178,4 +188,34 @@
val icon = checkBoxRoot.findViewByType<ImageView>()
assertThat(icon).hasColorFilter("#FF0000")
}
+
+ @Config(sdk = [29])
+ @Test
+ fun canTranslateCheckBox_onCheckedChange_null() = fakeCoroutineScope.runBlockingTest {
+ val rv = context.runAndTranslate {
+ CheckBox(
+ checked = true,
+ onCheckedChange = null,
+ text = "CheckBox",
+ )
+ }
+
+ val checkboxRoot = assertIs<ViewGroup>(context.applyRemoteViews(rv))
+ assertThat(checkboxRoot.hasOnClickListeners()).isFalse()
+ }
+
+ @Config(sdk = [29])
+ @Test
+ fun canTranslateCheckBox_onCheckedChange_withAction() = fakeCoroutineScope.runBlockingTest {
+ val rv = context.runAndTranslate {
+ CheckBox(
+ checked = true,
+ onCheckedChange = actionRunCallback<ActionCallback>(),
+ text = "CheckBox",
+ )
+ }
+
+ val checkboxRoot = assertIs<ViewGroup>(context.applyRemoteViews(rv))
+ assertThat(checkboxRoot.hasOnClickListeners()).isTrue()
+ }
}
\ No newline at end of file
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/SwitchTranslatorTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/SwitchTranslatorTest.kt
index 8b5766a..2e30f94 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/SwitchTranslatorTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/translators/SwitchTranslatorTest.kt
@@ -24,6 +24,8 @@
import androidx.glance.appwidget.ImageViewSubject.Companion.assertThat
import androidx.glance.appwidget.Switch
import androidx.glance.appwidget.SwitchColors
+import androidx.glance.appwidget.action.ActionCallback
+import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.appwidget.applyRemoteViews
import androidx.glance.appwidget.configurationContext
import androidx.glance.appwidget.findView
@@ -32,6 +34,7 @@
import androidx.glance.appwidget.unit.ColorProvider
import androidx.glance.unit.ColorProvider
import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
@@ -63,6 +66,7 @@
val rv = context.runAndTranslate {
Switch(
checked = false,
+ onCheckedChange = null,
text = "Switch",
colors = SwitchColors(
checkedThumbColor = ColorProvider(Color.Blue),
@@ -83,6 +87,7 @@
val rv = context.runAndTranslate {
Switch(
checked = true,
+ onCheckedChange = null,
text = "Switch",
colors = SwitchColors(
checkedThumbColor = ColorProvider(Color.Blue),
@@ -103,6 +108,7 @@
val rv = lightContext.runAndTranslate {
Switch(
checked = false,
+ onCheckedChange = null,
text = "Switch",
colors = SwitchColors(
checkedThumbColor = ColorProvider(day = Color.Blue, night = Color.Red),
@@ -126,6 +132,7 @@
val rv = darkContext.runAndTranslate {
Switch(
checked = false,
+ onCheckedChange = null,
text = "Switch",
colors = SwitchColors(
checkedThumbColor = ColorProvider(day = Color.Blue, night = Color.Red),
@@ -149,6 +156,7 @@
val rv = lightContext.runAndTranslate {
Switch(
checked = true,
+ onCheckedChange = null,
text = "Switch",
colors = SwitchColors(
checkedThumbColor = ColorProvider(day = Color.Blue, night = Color.Red),
@@ -172,6 +180,7 @@
val rv = darkContext.runAndTranslate {
Switch(
checked = true,
+ onCheckedChange = null,
text = "Switch",
colors = SwitchColors(
checkedThumbColor = ColorProvider(day = Color.Blue, night = Color.Red),
@@ -195,6 +204,7 @@
val rv = lightContext.runAndTranslate {
Switch(
checked = false,
+ onCheckedChange = null,
text = "Switch",
colors = SwitchColors(
thumbColor = R.color.my_switch_thumb_colors,
@@ -213,6 +223,7 @@
val rv = lightContext.runAndTranslate {
Switch(
checked = true,
+ onCheckedChange = null,
text = "Switch",
colors = SwitchColors(
thumbColor = R.color.my_switch_thumb_colors,
@@ -226,15 +237,43 @@
assertThat(switchRoot.trackImageView).hasColorFilter("#22040040")
}
+ @Test
+ fun canTranslateSwitch_onCheckedChange_null() = fakeCoroutineScope.runBlockingTest {
+ val rv = context.runAndTranslate {
+ Switch(
+ checked = true,
+ onCheckedChange = null,
+ text = "Switch",
+ )
+ }
+
+ val switchRoot = assertIs<ViewGroup>(context.applyRemoteViews(rv))
+ assertThat(switchRoot.hasOnClickListeners()).isFalse()
+ }
+
+ @Test
+ fun canTranslateSwitch_onCheckedChange_withAction() = fakeCoroutineScope.runBlockingTest {
+ val rv = context.runAndTranslate {
+ Switch(
+ checked = true,
+ onCheckedChange = actionRunCallback<ActionCallback>(),
+ text = "Switch",
+ )
+ }
+
+ val switchRoot = assertIs<ViewGroup>(context.applyRemoteViews(rv))
+ assertThat(switchRoot.hasOnClickListeners()).isTrue()
+ }
+
private val ViewGroup.thumbImageView: ImageView?
get() = findView {
shadowOf(it.drawable).createdFromResId ==
- androidx.glance.appwidget.R.drawable.switch_thumb_material
+ androidx.glance.appwidget.R.drawable.switch_thumb_animated
}
private val ViewGroup.trackImageView: ImageView?
get() = findView {
shadowOf(it.drawable).createdFromResId ==
- androidx.glance.appwidget.R.drawable.switch_track_material
+ androidx.glance.appwidget.R.drawable.switch_track
}
}
\ No newline at end of file
diff --git a/glance/glance-wear/api/current.txt b/glance/glance-wear/api/current.txt
index d1bb2da..66d8ba9 100644
--- a/glance/glance-wear/api/current.txt
+++ b/glance/glance-wear/api/current.txt
@@ -10,17 +10,48 @@
method public static androidx.glance.GlanceModifier border(androidx.glance.GlanceModifier, @DimenRes int width, androidx.glance.unit.ColorProvider color);
}
+ public final class CompositionLocalsKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.wear.TimeInterval> getLocalTimeInterval();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.wear.TimeInterval> LocalTimeInterval;
+ }
+
public abstract class GlanceTileService extends androidx.wear.tiles.TileService {
ctor public GlanceTileService();
method @androidx.compose.runtime.Composable public abstract void Content();
+ method public androidx.glance.wear.TimelineMode getTimelineMode();
method protected final com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources> onResourcesRequest(androidx.wear.tiles.RequestBuilders.ResourcesRequest requestParams);
method public void onStart(android.content.Intent? intent, int startId);
method protected final com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile> onTileRequest(androidx.wear.tiles.RequestBuilders.TileRequest requestParams);
+ property public androidx.glance.wear.TimelineMode timelineMode;
}
public final class NormalizeCompositionTreeKt {
}
+ public final class TimeInterval {
+ ctor public TimeInterval(optional java.time.Instant start, optional java.time.Instant end);
+ method public java.time.Instant component1();
+ method public java.time.Instant component2();
+ method public androidx.glance.wear.TimeInterval copy(java.time.Instant start, java.time.Instant end);
+ method public java.time.Instant getEnd();
+ method public java.time.Instant getStart();
+ property public final java.time.Instant end;
+ property public final java.time.Instant start;
+ }
+
+ public sealed interface TimelineMode {
+ }
+
+ public static final class TimelineMode.SingleEntry implements androidx.glance.wear.TimelineMode {
+ field public static final androidx.glance.wear.TimelineMode.SingleEntry INSTANCE;
+ }
+
+ public static final class TimelineMode.TimeBoundEntries implements androidx.glance.wear.TimelineMode {
+ ctor public TimelineMode.TimeBoundEntries(java.util.Set<androidx.glance.wear.TimeInterval> timeIntervals);
+ method public java.util.Set<androidx.glance.wear.TimeInterval> getTimeIntervals();
+ property public final java.util.Set<androidx.glance.wear.TimeInterval> timeIntervals;
+ }
+
public final class WearCompositionTranslatorKt {
}
diff --git a/glance/glance-wear/api/public_plus_experimental_current.txt b/glance/glance-wear/api/public_plus_experimental_current.txt
index d1bb2da..66d8ba9 100644
--- a/glance/glance-wear/api/public_plus_experimental_current.txt
+++ b/glance/glance-wear/api/public_plus_experimental_current.txt
@@ -10,17 +10,48 @@
method public static androidx.glance.GlanceModifier border(androidx.glance.GlanceModifier, @DimenRes int width, androidx.glance.unit.ColorProvider color);
}
+ public final class CompositionLocalsKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.wear.TimeInterval> getLocalTimeInterval();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.wear.TimeInterval> LocalTimeInterval;
+ }
+
public abstract class GlanceTileService extends androidx.wear.tiles.TileService {
ctor public GlanceTileService();
method @androidx.compose.runtime.Composable public abstract void Content();
+ method public androidx.glance.wear.TimelineMode getTimelineMode();
method protected final com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources> onResourcesRequest(androidx.wear.tiles.RequestBuilders.ResourcesRequest requestParams);
method public void onStart(android.content.Intent? intent, int startId);
method protected final com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile> onTileRequest(androidx.wear.tiles.RequestBuilders.TileRequest requestParams);
+ property public androidx.glance.wear.TimelineMode timelineMode;
}
public final class NormalizeCompositionTreeKt {
}
+ public final class TimeInterval {
+ ctor public TimeInterval(optional java.time.Instant start, optional java.time.Instant end);
+ method public java.time.Instant component1();
+ method public java.time.Instant component2();
+ method public androidx.glance.wear.TimeInterval copy(java.time.Instant start, java.time.Instant end);
+ method public java.time.Instant getEnd();
+ method public java.time.Instant getStart();
+ property public final java.time.Instant end;
+ property public final java.time.Instant start;
+ }
+
+ public sealed interface TimelineMode {
+ }
+
+ public static final class TimelineMode.SingleEntry implements androidx.glance.wear.TimelineMode {
+ field public static final androidx.glance.wear.TimelineMode.SingleEntry INSTANCE;
+ }
+
+ public static final class TimelineMode.TimeBoundEntries implements androidx.glance.wear.TimelineMode {
+ ctor public TimelineMode.TimeBoundEntries(java.util.Set<androidx.glance.wear.TimeInterval> timeIntervals);
+ method public java.util.Set<androidx.glance.wear.TimeInterval> getTimeIntervals();
+ property public final java.util.Set<androidx.glance.wear.TimeInterval> timeIntervals;
+ }
+
public final class WearCompositionTranslatorKt {
}
diff --git a/glance/glance-wear/api/restricted_current.txt b/glance/glance-wear/api/restricted_current.txt
index d1bb2da..66d8ba9 100644
--- a/glance/glance-wear/api/restricted_current.txt
+++ b/glance/glance-wear/api/restricted_current.txt
@@ -10,17 +10,48 @@
method public static androidx.glance.GlanceModifier border(androidx.glance.GlanceModifier, @DimenRes int width, androidx.glance.unit.ColorProvider color);
}
+ public final class CompositionLocalsKt {
+ method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.wear.TimeInterval> getLocalTimeInterval();
+ property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.glance.wear.TimeInterval> LocalTimeInterval;
+ }
+
public abstract class GlanceTileService extends androidx.wear.tiles.TileService {
ctor public GlanceTileService();
method @androidx.compose.runtime.Composable public abstract void Content();
+ method public androidx.glance.wear.TimelineMode getTimelineMode();
method protected final com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.ResourceBuilders.Resources> onResourcesRequest(androidx.wear.tiles.RequestBuilders.ResourcesRequest requestParams);
method public void onStart(android.content.Intent? intent, int startId);
method protected final com.google.common.util.concurrent.ListenableFuture<androidx.wear.tiles.TileBuilders.Tile> onTileRequest(androidx.wear.tiles.RequestBuilders.TileRequest requestParams);
+ property public androidx.glance.wear.TimelineMode timelineMode;
}
public final class NormalizeCompositionTreeKt {
}
+ public final class TimeInterval {
+ ctor public TimeInterval(optional java.time.Instant start, optional java.time.Instant end);
+ method public java.time.Instant component1();
+ method public java.time.Instant component2();
+ method public androidx.glance.wear.TimeInterval copy(java.time.Instant start, java.time.Instant end);
+ method public java.time.Instant getEnd();
+ method public java.time.Instant getStart();
+ property public final java.time.Instant end;
+ property public final java.time.Instant start;
+ }
+
+ public sealed interface TimelineMode {
+ }
+
+ public static final class TimelineMode.SingleEntry implements androidx.glance.wear.TimelineMode {
+ field public static final androidx.glance.wear.TimelineMode.SingleEntry INSTANCE;
+ }
+
+ public static final class TimelineMode.TimeBoundEntries implements androidx.glance.wear.TimelineMode {
+ ctor public TimelineMode.TimeBoundEntries(java.util.Set<androidx.glance.wear.TimeInterval> timeIntervals);
+ method public java.util.Set<androidx.glance.wear.TimeInterval> getTimeIntervals();
+ property public final java.util.Set<androidx.glance.wear.TimeInterval> timeIntervals;
+ }
+
public final class WearCompositionTranslatorKt {
}
diff --git a/glance/glance-wear/build.gradle b/glance/glance-wear/build.gradle
index eb1fdf2..0fc530b 100644
--- a/glance/glance-wear/build.gradle
+++ b/glance/glance-wear/build.gradle
@@ -44,6 +44,7 @@
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-service:2.3.1"
+ testImplementation(project(":core:core-ktx"))
testImplementation(libs.testCore)
testImplementation(libs.testRules)
testImplementation(libs.testRunner)
@@ -59,6 +60,7 @@
testImplementation("androidx.wear.tiles:tiles-renderer:1.0.0-alpha12")
testImplementation("androidx.wear.tiles:tiles-testing:1.0.0-alpha12")
+ androidTestImplementation(project(":core:core-ktx"))
androidTestImplementation("androidx.core:core:1.7.0")
androidTestImplementation(project(":test:screenshot:screenshot"))
androidTestImplementation("androidx.wear.tiles:tiles-renderer:1.0.0-alpha12")
diff --git a/glance/glance-wear/src/androidAndroidTest/kotlin/androidx/glance/wear/ScreenshotTests.kt b/glance/glance-wear/src/androidAndroidTest/kotlin/androidx/glance/wear/ScreenshotTests.kt
index e9bda4b..9552fab 100644
--- a/glance/glance-wear/src/androidAndroidTest/kotlin/androidx/glance/wear/ScreenshotTests.kt
+++ b/glance/glance-wear/src/androidAndroidTest/kotlin/androidx/glance/wear/ScreenshotTests.kt
@@ -29,6 +29,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
+import androidx.core.graphics.drawable.toBitmap
import androidx.glance.Applier
import androidx.glance.GlanceModifier
import androidx.glance.Image
@@ -81,10 +82,12 @@
var screenshotRule = AndroidXScreenshotTestRule("glance/glance-wear")
private lateinit var fakeCoroutineScope: TestCoroutineScope
+ private lateinit var testBitmap: Bitmap
@Before
fun setUp() {
fakeCoroutineScope = TestCoroutineScope()
+ testBitmap = getApplicationContext<Context>().getDrawable(R.drawable.oval)!!.toBitmap()
}
@Test
@@ -229,7 +232,7 @@
)
Spacer(modifier = GlanceModifier.height(10.dp))
Image(
- provider = ImageProvider(R.drawable.oval),
+ provider = ImageProvider(testBitmap),
contentDescription = "Oval-fit",
modifier = GlanceModifier.size(50.dp),
contentScale = ContentScale.Fit
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt b/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/CompositionLocals.kt
similarity index 68%
copy from compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt
copy to glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/CompositionLocals.kt
index d721bff..99ca2cb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemsProvider.kt
+++ b/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/CompositionLocals.kt
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package androidx.compose.foundation.lazy
+package androidx.glance.wear
-import androidx.compose.foundation.lazy.layout.LazyLayoutItemsProvider
+import androidx.compose.runtime.staticCompositionLocalOf
-internal interface LazyListItemsProvider : LazyLayoutItemsProvider {
- /** The list of indexes of the sticky header items */
- val headerIndexes: List<Int>
-}
+/**
+ * Time interval of the glance tile UI being generated.
+ */
+public val LocalTimeInterval =
+ staticCompositionLocalOf<TimeInterval?> { error("No default time interval") }
\ No newline at end of file
diff --git a/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/GlanceTileService.kt b/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/GlanceTileService.kt
index 07d72f0..cf6e35a 100644
--- a/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/GlanceTileService.kt
+++ b/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/GlanceTileService.kt
@@ -22,6 +22,7 @@
import androidx.compose.runtime.BroadcastFrameClock
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composition
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Recomposer
import androidx.glance.Applier
import androidx.glance.layout.EmittableBox
@@ -96,25 +97,37 @@
super.onStart(intent, startId)
}
- private suspend fun runComposition(): CompositionResult = coroutineScope {
- val root = EmittableBox()
- val applier = Applier(root)
- val recomposer = Recomposer(currentCoroutineContext())
- val composition = Composition(applier, recomposer)
+ private suspend fun runComposition(
+ timeInterval: TimeInterval? = null
+ ): CompositionResult =
+ coroutineScope {
+ val root = EmittableBox()
+ val applier = Applier(root)
+ val recomposer = Recomposer(currentCoroutineContext())
+ val composition = Composition(applier, recomposer)
- composition.setContent { Content() }
+ composition.setContent {
+ CompositionLocalProvider(
+ LocalTimeInterval provides timeInterval
+ ) { Content() }
+ }
- launch { recomposer.runRecomposeAndApplyChanges() }
+ launch { recomposer.runRecomposeAndApplyChanges() }
- recomposer.close()
- recomposer.join()
+ recomposer.close()
+ recomposer.join()
- normalizeCompositionTree(this@GlanceTileService, root)
+ normalizeCompositionTree(this@GlanceTileService, root)
- translateTopLevelComposition(this@GlanceTileService, root)
- }
+ translateTopLevelComposition(this@GlanceTileService, root)
+ }
- /** Implement with the layout to use in your Tile. */
+ /**
+ * Defines the handling of timeline
+ */
+ public open val timelineMode: TimelineMode = TimelineMode.SingleEntry
+
+ /** Override this method to set the layout to use in your Tile. */
@Composable
public abstract fun Content()
@@ -127,21 +140,46 @@
final override fun onTileRequest(
requestParams: RequestBuilders.TileRequest
): ListenableFuture<TileBuilders.Tile> = coroutineScope.future {
- val content = runComposition().layout
+ val timelineBuilders = TimelineBuilders.Timeline.Builder()
+ if (timelineMode === TimelineMode.SingleEntry) {
+ val content = runComposition().layout
+
+ timelineBuilders
+ .addTimelineEntry(
+ TimelineBuilders.TimelineEntry.Builder()
+ .setLayout(
+ LayoutElementBuilders.Layout.Builder()
+ .setRoot(content)
+ .build()
+ ).build()
+ )
+ } else { // timelineMode is TimelineMode.TimeBoundEntries
+ val timeIntervals = (timelineMode as TimelineMode.TimeBoundEntries).timeIntervals
+ timeIntervals.forEach {
+ val result = runComposition(it).layout
+
+ timelineBuilders
+ .addTimelineEntry(
+ TimelineBuilders.TimelineEntry.Builder()
+ .setValidity(
+ TimelineBuilders.TimeInterval.Builder()
+ .setStartMillis(it.start.toEpochMilli())
+ .setEndMillis(it.end.toEpochMilli())
+ .build()
+ )
+ .setLayout(
+ LayoutElementBuilders.Layout.Builder()
+ .setRoot(result)
+ .build()
+ ).build()
+ )
+ }
+ }
TileBuilders.Tile.Builder()
.setResourcesVersion(ResourcesVersion)
- .setTimeline(
- TimelineBuilders.Timeline.Builder()
- .addTimelineEntry(
- TimelineBuilders.TimelineEntry.Builder()
- .setLayout(
- LayoutElementBuilders.Layout.Builder()
- .setRoot(content)
- .build()
- ).build()
- ).build()
- ).build()
+ .setTimeline(timelineBuilders.build())
+ .build()
}
/**
@@ -153,7 +191,21 @@
final override fun onResourcesRequest(
requestParams: RequestBuilders.ResourcesRequest
): ListenableFuture<ResourceBuilders.Resources> = coroutineScope.future {
- runComposition().resources.setVersion(ResourcesVersion).build()
+ var resourceBuilder: ResourceBuilders.Resources.Builder
+ if (timelineMode === TimelineMode.SingleEntry) {
+ resourceBuilder = runComposition().resources
+ } else {
+ timelineMode is TimelineMode.TimeBoundEntries
+ resourceBuilder = ResourceBuilders.Resources.Builder()
+ val timeIntervals = (timelineMode as TimelineMode.TimeBoundEntries).timeIntervals
+ timeIntervals.forEach {
+ val result = runComposition(it).resources
+ result.build().idToImageMapping.forEach { res ->
+ resourceBuilder.addIdToImageMapping(res.key, res.value)
+ }
+ }
+ }
+ resourceBuilder.setVersion(ResourcesVersion).build()
}
@VisibleForTesting
diff --git a/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/TimelineMode.kt b/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/TimelineMode.kt
new file mode 100644
index 0000000..4e92fa6
--- /dev/null
+++ b/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/TimelineMode.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.wear
+
+import java.time.Instant
+
+/**
+ * TimeInterval class defines a period from [start] to [end]
+ *
+ * @param start The start time of the time interval
+ * @param end The end time of the time interval
+ */
+public data class TimeInterval(
+ val start: Instant = Instant.ofEpochMilli(0),
+ val end: Instant = Instant.ofEpochMilli(Long.MAX_VALUE)
+) {
+ init {
+ require(end > start) {
+ "End time shall come after start time to form a valid interval"
+ }
+ }
+}
+
+public sealed interface TimelineMode {
+ /**
+ * The [GlanceTileService] provides a single UI.
+ * The layout is fixed, and only the information inside the layout changes.
+ */
+ public object SingleEntry : TimelineMode {
+ public override fun toString(): String = "TimelineMode: SingleEntry"
+ }
+
+ /**
+ * The [GlanceTileService] provides a UI for a fixed set of time intervals
+ *
+ * @param timeIntervals Used to build the list of time intervals, the list must not be empty.
+ */
+ public class TimeBoundEntries(val timeIntervals: Set<TimeInterval>) : TimelineMode {
+ init {
+ require(timeIntervals.isNotEmpty()) { "The set of time intervals cannot be empty" }
+ }
+
+ public override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as TimeBoundEntries
+
+ if (timeIntervals != other.timeIntervals) return false
+
+ return true
+ }
+
+ public override fun hashCode(): Int = timeIntervals.hashCode()
+
+ public override fun toString(): String =
+ "TimelineMode.TimeBoundEntries(timeIntervals=$timeIntervals)"
+ }
+}
\ No newline at end of file
diff --git a/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/WearCompositionTranslator.kt b/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/WearCompositionTranslator.kt
index f78e8a6..a312473 100644
--- a/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/WearCompositionTranslator.kt
+++ b/glance/glance-wear/src/androidMain/kotlin/androidx/glance/wear/WearCompositionTranslator.kt
@@ -17,12 +17,14 @@
package androidx.glance.wear
import android.content.Context
+import android.graphics.Bitmap
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.unit.dp
import androidx.glance.AndroidResourceImageProvider
import androidx.glance.BackgroundModifier
+import androidx.glance.BitmapImageProvider
import androidx.glance.Emittable
import androidx.glance.EmittableButton
import androidx.glance.EmittableImage
@@ -86,6 +88,8 @@
import androidx.wear.tiles.LayoutElementBuilders.VerticalAlignment
import androidx.wear.tiles.ModifiersBuilders
import androidx.wear.tiles.ResourceBuilders
+import java.io.ByteArrayOutputStream
+import java.util.Arrays
@VerticalAlignment
private fun Alignment.Vertical.toProto(): Int =
@@ -133,6 +137,7 @@
else -> error("Unsupported color provider: $this")
}
+// TODO: handle parameters
private fun LaunchActivityAction.toProto(context: Context): ActionBuilders.LaunchAction =
ActionBuilders.LaunchAction.Builder()
.setAndroidActivity(
@@ -397,17 +402,40 @@
resourceBuilder: ResourceBuilders.Resources.Builder,
element: EmittableImage
): LayoutElementBuilders.LayoutElement {
-
- val resId = (element.provider as AndroidResourceImageProvider).resId
- val mappedResId = "android_$resId"
- resourceBuilder.addIdToImageMapping(
- mappedResId,
- ResourceBuilders.ImageResource.Builder().setAndroidResourceByResId(
- ResourceBuilders.AndroidImageResourceByResId.Builder()
- .setResourceId(resId)
- .build()
- ).build()
- )
+ var mappedResId: String
+ when (element.provider) {
+ is AndroidResourceImageProvider -> {
+ val resId = (element.provider as AndroidResourceImageProvider).resId
+ mappedResId = "android_$resId"
+ resourceBuilder.addIdToImageMapping(
+ mappedResId,
+ ResourceBuilders.ImageResource.Builder().setAndroidResourceByResId(
+ ResourceBuilders.AndroidImageResourceByResId.Builder()
+ .setResourceId(resId)
+ .build()
+ ).build()
+ )
+ }
+ is BitmapImageProvider -> {
+ val bitmap = (element.provider as BitmapImageProvider).bitmap
+ val buffer = ByteArrayOutputStream().apply {
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, this)
+ }.toByteArray()
+ mappedResId = "android_${Arrays.hashCode(buffer)}"
+ resourceBuilder.addIdToImageMapping(
+ mappedResId,
+ ResourceBuilders.ImageResource.Builder().setInlineResource(
+ ResourceBuilders.InlineImageResource.Builder()
+ .setWidthPx(bitmap.width)
+ .setHeightPx(bitmap.height)
+ .setData(buffer)
+ .build()
+ ).build()
+ )
+ }
+ else ->
+ throw IllegalArgumentException("An unsupported ImageProvider type was used")
+ }
val imageBuilder = LayoutElementBuilders.Image.Builder()
.setWidth(element.modifier.getWidth(context).toImageDimension())
diff --git a/glance/glance-wear/src/test/kotlin/androidx/glance/wear/GlanceTileServiceTest.kt b/glance/glance-wear/src/test/kotlin/androidx/glance/wear/GlanceTileServiceTest.kt
index fadf753..7133c06 100644
--- a/glance/glance-wear/src/test/kotlin/androidx/glance/wear/GlanceTileServiceTest.kt
+++ b/glance/glance-wear/src/test/kotlin/androidx/glance/wear/GlanceTileServiceTest.kt
@@ -18,9 +18,17 @@
import android.os.Looper
import androidx.compose.runtime.Composable
+import androidx.compose.ui.unit.dp
+import androidx.glance.GlanceModifier
+import androidx.glance.Image
+import androidx.glance.ImageProvider
+import androidx.glance.layout.ContentScale
+import androidx.glance.layout.size
import androidx.glance.text.Text
+import androidx.glance.wear.test.R
import androidx.wear.tiles.LayoutElementBuilders
import androidx.wear.tiles.RequestBuilders
+import androidx.wear.tiles.TimelineBuilders
import androidx.wear.tiles.testing.TestTileClient
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineDispatcher
@@ -33,6 +41,7 @@
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows.shadowOf
+import java.time.Instant
import kotlin.test.assertIs
@OptIn(ExperimentalCoroutinesApi::class, ExperimentalStdlibApi::class)
@@ -41,23 +50,30 @@
private lateinit var fakeCoroutineScope: TestCoroutineScope
private lateinit var tileService: TestGlanceTileService
private lateinit var tileServiceClient: TestTileClient<GlanceTileService>
+ private lateinit var tileServiceWithTimeline: TestGlanceTileServiceWithTimeline
+ private lateinit var tileServiceClientWithTimeline: TestTileClient<GlanceTileService>
@Before
fun setUp() {
fakeCoroutineScope = TestCoroutineScope()
+
tileService = TestGlanceTileService()
tileServiceClient = TestTileClient(
tileService,
fakeCoroutineScope,
fakeCoroutineScope.coroutineContext[CoroutineDispatcher]!!
)
+
+ tileServiceWithTimeline = TestGlanceTileServiceWithTimeline()
+ tileServiceClientWithTimeline = TestTileClient(
+ tileServiceWithTimeline,
+ fakeCoroutineScope,
+ fakeCoroutineScope.coroutineContext[CoroutineDispatcher]!!
+ )
}
@Test
fun tileProviderReturnsTile() = fakeCoroutineScope.runBlockingTest {
- // Add the composition to the service.
- tileService.actualContent = { Text("Hello World!") }
-
// Request is currently un-used, provide an empty one.
val tileRequest = RequestBuilders.TileRequest.Builder().build()
@@ -79,16 +95,62 @@
// It always emits a box as the root-level layout.
val box = assertIs<LayoutElementBuilders.Box>(entry.layout!!.root!!)
- assertThat(box.contents).hasSize(1)
+ assertThat(box.contents).hasSize(2)
val text = assertIs<LayoutElementBuilders.Text>(box.contents[0])
assertThat(text.text!!.value).isEqualTo("Hello World!")
}
@Test
+ fun tileProviderReturnsTimelineTile() = fakeCoroutineScope.runBlockingTest {
+ // Request is currently un-used, provide an empty one.
+ val tileRequest = RequestBuilders.TileRequest.Builder().build()
+
+ // Requests need to be split; we need to allow Robolectric to schedule the service calls on
+ // the main looper, so we can't just do requestTile().await().
+ val tileFuture = tileServiceClientWithTimeline.requestTile(tileRequest)
+ shadowOf(Looper.getMainLooper()).idle()
+ val tile = tileFuture.await()
+
+ // Just uses a simple resource version for now.
+ assertThat(tile.resourcesVersion).isEqualTo(GlanceTileService.ResourcesVersion)
+
+ // No freshness interval (for now)
+ assertThat(tile.freshnessIntervalMillis).isEqualTo(0)
+
+ assertThat(tile.timeline!!.timelineEntries).hasSize(4)
+
+ checkTimelineEntry(
+ tile.timeline!!.timelineEntries[0],
+ 0,
+ Long.MAX_VALUE,
+ "No event"
+ )
+
+ checkTimelineEntry(
+ tile.timeline!!.timelineEntries[1],
+ time1.toEpochMilli(),
+ time2.toEpochMilli(),
+ "Coffee"
+ )
+
+ checkTimelineEntry(
+ tile.timeline!!.timelineEntries[2],
+ time2.toEpochMilli(),
+ time3.toEpochMilli(),
+ "Work"
+ )
+
+ checkTimelineEntry(
+ tile.timeline!!.timelineEntries[3],
+ time4.toEpochMilli(),
+ Long.MAX_VALUE,
+ "Dinner"
+ )
+ }
+
+ @Test
fun tileProviderReturnsResources() = fakeCoroutineScope.runBlockingTest {
- // The service doesn't use resources right now, but it must correctly respond to
- // onResourcesRequest. Just ensure that the version is set correctly.
val resourcesRequest = RequestBuilders.ResourcesRequest.Builder().build()
val resourcesFuture = tileServiceClient.requestResources(resourcesRequest)
@@ -96,14 +158,91 @@
val resources = resourcesFuture.await()
assertThat(resources.version).isEqualTo(GlanceTileService.ResourcesVersion)
+ assertThat(resources.idToImageMapping.size).isEqualTo(1)
+ assertThat(resources.idToImageMapping.containsKey("android_" + R.drawable.oval)).isTrue()
+ }
+
+ @Test
+ fun tileProviderReturnsTimelineResources() = fakeCoroutineScope.runBlockingTest {
+ val resourcesRequest = RequestBuilders.ResourcesRequest.Builder().build()
+
+ val resourcesFuture = tileServiceClientWithTimeline.requestResources(resourcesRequest)
+ shadowOf(Looper.getMainLooper()).idle()
+ val resources = resourcesFuture.await()
+
+ assertThat(resources.version).isEqualTo(GlanceTileService.ResourcesVersion)
+ assertThat(resources.idToImageMapping.size).isEqualTo(2)
+ assertThat(resources.idToImageMapping.containsKey("android_" + R.drawable.oval)).isTrue()
+ assertThat(
+ resources.idToImageMapping.containsKey("android_" + R.drawable.ic_launcher_background)
+ ).isTrue()
+ }
+
+ private fun checkTimelineEntry(
+ entry: TimelineBuilders.TimelineEntry,
+ startMillis: Long,
+ endMillis: Long,
+ textValue: String
+ ) {
+ assertThat(entry.validity!!.startMillis).isEqualTo(startMillis)
+ assertThat(entry.validity!!.endMillis).isEqualTo(endMillis)
+ var box = assertIs<LayoutElementBuilders.Box>(entry.layout!!.root!!)
+ var text = assertIs<LayoutElementBuilders.Text>(box.contents[0])
+ assertThat(text.text!!.value).isEqualTo(textValue)
}
private inner class TestGlanceTileService : GlanceTileService() {
- var actualContent: @Composable () -> Unit = {}
-
@Composable
override fun Content() {
- actualContent()
+ Text("Hello World!")
+ Image(
+ provider = ImageProvider(R.drawable.oval),
+ contentDescription = "Oval",
+ modifier = GlanceModifier.size(40.dp),
+ contentScale = ContentScale.FillBounds
+ )
}
}
+
+ private inner class TestGlanceTileServiceWithTimeline : GlanceTileService() {
+ override val timelineMode = testTimelineMode
+
+ @Composable
+ override fun Content() {
+ when (LocalTimeInterval.current) {
+ testTimelineMode.timeIntervals.elementAt(0) -> { Text("No event") }
+ testTimelineMode.timeIntervals.elementAt(1) -> {
+ Text("Coffee")
+ Image(
+ provider = ImageProvider(R.drawable.oval),
+ contentDescription = "Oval",
+ modifier = GlanceModifier.size(40.dp),
+ contentScale = ContentScale.FillBounds
+ )
+ }
+ testTimelineMode.timeIntervals.elementAt(2) -> {
+ Text("Work")
+ Image(
+ provider = ImageProvider(R.drawable.ic_launcher_background),
+ contentDescription = "Icon",
+ modifier = GlanceModifier.size(40.dp),
+ )
+ }
+ testTimelineMode.timeIntervals.elementAt(3) -> { Text("Dinner") }
+ }
+ }
+ }
+
+ private companion object {
+ private val time1 = Instant.parse("2021-11-12T13:15:30.00Z")
+ private val time2 = Instant.parse("2021-11-12T13:45:30.00Z")
+ private val time3 = Instant.parse("2021-11-12T17:45:30.00Z")
+ private val time4 = Instant.parse("2021-11-12T18:30:30.00Z")
+ val testTimelineMode = TimelineMode.TimeBoundEntries(setOf(
+ TimeInterval(),
+ TimeInterval(time1, time2),
+ TimeInterval(time2, time3),
+ TimeInterval(time4)
+ ))
+ }
}
\ No newline at end of file
diff --git a/glance/glance-wear/src/test/kotlin/androidx/glance/wear/TimelineModeTest.kt b/glance/glance-wear/src/test/kotlin/androidx/glance/wear/TimelineModeTest.kt
new file mode 100644
index 0000000..d044d94
--- /dev/null
+++ b/glance/glance-wear/src/test/kotlin/androidx/glance/wear/TimelineModeTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.glance.wear
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import java.time.Instant
+
+class TimelineModeTest {
+
+ @Test
+ fun timeboundEntriesTest() {
+ val time1 = Instant.parse("2021-11-12T13:15:30.00Z")
+ val time2 = Instant.parse("2021-11-12T13:45:30.00Z")
+ val time3 = Instant.parse("2021-11-12T17:45:30.00Z")
+ val time4 = Instant.parse("2021-11-12T18:30:30.00Z")
+
+ val timeBoundEntries = TimelineMode.TimeBoundEntries(setOf(
+ TimeInterval(),
+ TimeInterval(time1, time2),
+ TimeInterval(time2, time3),
+ TimeInterval(time4)
+ ))
+ val intervals = timeBoundEntries.timeIntervals
+
+ assertThat(intervals.size).isEqualTo(4)
+
+ assertThat(intervals.elementAt(0).start.toEpochMilli()).isEqualTo(0)
+ assertThat(intervals.elementAt(0).end.toEpochMilli()).isEqualTo(Long.MAX_VALUE)
+
+ assertThat(intervals.elementAt(1).start).isEqualTo(time1)
+ assertThat(intervals.elementAt(1).end).isEqualTo(time2)
+
+ assertThat(intervals.elementAt(2).start).isEqualTo(time2)
+ assertThat(intervals.elementAt(2).end).isEqualTo(time3)
+
+ assertThat(intervals.elementAt(3).start).isEqualTo(time4)
+ assertThat(intervals.elementAt(3).end.toEpochMilli()).isEqualTo(Long.MAX_VALUE)
+ }
+}
\ No newline at end of file
diff --git a/glance/glance-wear/src/test/kotlin/androidx/glance/wear/WearCompositionTranslatorTest.kt b/glance/glance-wear/src/test/kotlin/androidx/glance/wear/WearCompositionTranslatorTest.kt
index c133aaa..74efee7 100644
--- a/glance/glance-wear/src/test/kotlin/androidx/glance/wear/WearCompositionTranslatorTest.kt
+++ b/glance/glance-wear/src/test/kotlin/androidx/glance/wear/WearCompositionTranslatorTest.kt
@@ -18,11 +18,13 @@
import android.app.Activity
import android.content.Context
+import android.graphics.Bitmap
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.core.graphics.drawable.toBitmap
import androidx.glance.Button
import androidx.glance.GlanceModifier
import androidx.glance.Image
@@ -72,6 +74,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
+import java.io.ByteArrayOutputStream
+import java.util.Arrays
import kotlin.test.assertIs
@OptIn(ExperimentalCoroutinesApi::class)
@@ -134,8 +138,10 @@
@Test
fun canTranslatePaddingModifier() = fakeCoroutineScope.runBlockingTest {
val content = runAndTranslate {
- Box(modifier =
- GlanceModifier.padding(start = 1.dp, top = 2.dp, end = 3.dp, bottom = 4.dp)) {}
+ Box(
+ modifier =
+ GlanceModifier.padding(start = 1.dp, top = 2.dp, end = 3.dp, bottom = 4.dp)
+ ) {}
}.layout
val innerBox =
@@ -151,14 +157,18 @@
@Test
fun canTranslateBorderModifier() = fakeCoroutineScope.runBlockingTest {
val content = runAndTranslate {
- Box(modifier = GlanceModifier.border(
+ Box(
+ modifier = GlanceModifier.border(
width = 3.dp,
color = ColorProvider(color = Color.Blue)
- )) {}
- Box(modifier = GlanceModifier.border(
+ )
+ ) {}
+ Box(
+ modifier = GlanceModifier.border(
width = R.dimen.dimension1,
- color = ColorProvider(color = Color.Red)
- )) {}
+ color = ColorProvider(color = Color.Red)
+ )
+ ) {}
}.layout
val innerBox1 =
@@ -556,35 +566,61 @@
@Test
fun canTranslateImage() = fakeCoroutineScope.runBlockingTest {
+ val context = getApplicationContext<Context>()
+ val bitmap = context.getDrawable(R.drawable.oval)!!.toBitmap()
val compositionResult = runAndTranslate {
- Image(
- provider = ImageProvider(R.drawable.oval),
- contentDescription = "Oval",
- modifier = GlanceModifier.width(R.dimen.dimension1).height(R.dimen.dimension2),
- contentScale = ContentScale.FillBounds
- )
+ Image(
+ provider = ImageProvider(R.drawable.oval),
+ contentDescription = "Oval",
+ modifier = GlanceModifier.width(R.dimen.dimension1).height(R.dimen.dimension2),
+ contentScale = ContentScale.FillBounds
+ )
+ Image(
+ provider = ImageProvider(bitmap),
+ contentDescription = "OvalBitmap",
+ modifier = GlanceModifier.width(R.dimen.dimension1).height(R.dimen.dimension2),
+ contentScale = ContentScale.Crop
+ )
}
val content = compositionResult.layout
val resources = compositionResult.resources
- val image = (content as LayoutElementBuilders.Box).contents[0] as
+ val image1 = (content as LayoutElementBuilders.Box).contents[0] as
LayoutElementBuilders.Image
- val context = getApplicationContext<Context>()
- assertThat((image.width as DimensionBuilders.DpProp).value).isEqualTo(
+ assertThat((image1.width as DimensionBuilders.DpProp).value).isEqualTo(
context.resources.getDimension(R.dimen.dimension1)
)
- assertThat((image.height as DimensionBuilders.DpProp).value).isEqualTo(
- context.resources.getDimension(R.dimen.dimension2)
+ assertThat((image1.height as DimensionBuilders.DpProp).value).isEqualTo(
+ context.resources.getDimension(R.dimen.dimension2)
)
- assertThat(image.contentScaleMode!!.value).isEqualTo(
+ assertThat(image1.contentScaleMode!!.value).isEqualTo(
LayoutElementBuilders.CONTENT_SCALE_MODE_FILL_BOUNDS
)
- assertThat(image.resourceId!!.value).isEqualTo("android_" + R.drawable.oval)
+ val mappedId1 = "android_" + R.drawable.oval
+ assertThat(image1.resourceId!!.value).isEqualTo(mappedId1)
- assertThat(resources.build().idToImageMapping.containsKey(R.drawable.oval.toString()))
+ assertThat(image1.modifiers!!.semantics!!.contentDescription).isEqualTo("Oval")
- assertThat(image.modifiers!!.semantics!!.contentDescription).isEqualTo("Oval")
+ val image2 = content.contents[1] as LayoutElementBuilders.Image
+ assertThat(image2.contentScaleMode!!.value).isEqualTo(
+ LayoutElementBuilders.CONTENT_SCALE_MODE_CROP
+ )
+
+ val buffer = ByteArrayOutputStream().apply {
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, this) }
+ .toByteArray()
+ val mappedId2 = "android_" + Arrays.hashCode(buffer)
+ assertThat(image2.resourceId!!.value).isEqualTo(mappedId2)
+ assertThat(image2.modifiers!!.semantics!!.contentDescription).isEqualTo("OvalBitmap")
+
+ val idToImageMap = resources.build().idToImageMapping
+ assertThat(idToImageMap.containsKey(mappedId1)).isTrue()
+ assertThat(idToImageMap[mappedId1]!!.androidResourceByResId).isNotNull()
+ assertThat(idToImageMap[mappedId1]!!.inlineResource).isNull()
+ assertThat(idToImageMap.containsKey(mappedId2)).isTrue()
+ assertThat(idToImageMap[mappedId2]!!.inlineResource).isNotNull()
+ assertThat(idToImageMap[mappedId2]!!.androidResourceByResId).isNull()
}
@Test
diff --git a/glance/glance-wear/src/test/res/drawable/ic_launcher_background.xml b/glance/glance-wear/src/test/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..ca3826a
--- /dev/null
+++ b/glance/glance-wear/src/test/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector
+ android:height="108dp"
+ android:width="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z"/>
+ <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+</vector>
diff --git a/glance/glance/api/current.txt b/glance/glance/api/current.txt
index 56e1253..5955625 100644
--- a/glance/glance/api/current.txt
+++ b/glance/glance/api/current.txt
@@ -117,12 +117,13 @@
method public static androidx.glance.action.MutableActionParameters mutableActionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
method public static androidx.glance.action.MutableActionParameters toMutableParameters(androidx.glance.action.ActionParameters);
method public static androidx.glance.action.ActionParameters toParameters(androidx.glance.action.ActionParameters);
+ method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
}
public final class LaunchActivityActionKt {
- method public static androidx.glance.action.Action actionLaunchActivity(android.content.ComponentName componentName);
- method public static <T extends android.app.Activity> androidx.glance.action.Action actionLaunchActivity(Class<T> activity);
- method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action! actionLaunchActivity();
+ method public static androidx.glance.action.Action actionLaunchActivity(android.content.ComponentName componentName, optional androidx.glance.action.ActionParameters parameters);
+ method public static <T extends android.app.Activity> androidx.glance.action.Action actionLaunchActivity(Class<T> activity, optional androidx.glance.action.ActionParameters parameters);
+ method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action! actionLaunchActivity(optional androidx.glance.action.ActionParameters parameters);
}
public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
@@ -276,15 +277,15 @@
package androidx.glance.state {
- public final class GlanceState {
- method public suspend <T> Object? getValue(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, String fileName, kotlin.coroutines.Continuation<? super T> p);
- method public suspend <T> Object? updateValue(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, String fileName, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateBlock, kotlin.coroutines.Continuation<? super T> p);
- field public static final androidx.glance.state.GlanceState INSTANCE;
+ public interface GlanceStateDefinition<T> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>> p);
+ method public java.io.File getLocation(android.content.Context context, String fileKey);
}
- public interface GlanceStateDefinition<T> {
- method public suspend <T> Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>> p);
+ public final class PreferencesGlanceStateDefinition implements androidx.glance.state.GlanceStateDefinition<androidx.datastore.preferences.core.Preferences> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>> p);
method public java.io.File getLocation(android.content.Context context, String fileKey);
+ field public static final androidx.glance.state.PreferencesGlanceStateDefinition INSTANCE;
}
}
diff --git a/glance/glance/api/public_plus_experimental_current.txt b/glance/glance/api/public_plus_experimental_current.txt
index 56e1253..5955625 100644
--- a/glance/glance/api/public_plus_experimental_current.txt
+++ b/glance/glance/api/public_plus_experimental_current.txt
@@ -117,12 +117,13 @@
method public static androidx.glance.action.MutableActionParameters mutableActionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
method public static androidx.glance.action.MutableActionParameters toMutableParameters(androidx.glance.action.ActionParameters);
method public static androidx.glance.action.ActionParameters toParameters(androidx.glance.action.ActionParameters);
+ method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
}
public final class LaunchActivityActionKt {
- method public static androidx.glance.action.Action actionLaunchActivity(android.content.ComponentName componentName);
- method public static <T extends android.app.Activity> androidx.glance.action.Action actionLaunchActivity(Class<T> activity);
- method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action! actionLaunchActivity();
+ method public static androidx.glance.action.Action actionLaunchActivity(android.content.ComponentName componentName, optional androidx.glance.action.ActionParameters parameters);
+ method public static <T extends android.app.Activity> androidx.glance.action.Action actionLaunchActivity(Class<T> activity, optional androidx.glance.action.ActionParameters parameters);
+ method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action! actionLaunchActivity(optional androidx.glance.action.ActionParameters parameters);
}
public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
@@ -276,15 +277,15 @@
package androidx.glance.state {
- public final class GlanceState {
- method public suspend <T> Object? getValue(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, String fileName, kotlin.coroutines.Continuation<? super T> p);
- method public suspend <T> Object? updateValue(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, String fileName, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateBlock, kotlin.coroutines.Continuation<? super T> p);
- field public static final androidx.glance.state.GlanceState INSTANCE;
+ public interface GlanceStateDefinition<T> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>> p);
+ method public java.io.File getLocation(android.content.Context context, String fileKey);
}
- public interface GlanceStateDefinition<T> {
- method public suspend <T> Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>> p);
+ public final class PreferencesGlanceStateDefinition implements androidx.glance.state.GlanceStateDefinition<androidx.datastore.preferences.core.Preferences> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>> p);
method public java.io.File getLocation(android.content.Context context, String fileKey);
+ field public static final androidx.glance.state.PreferencesGlanceStateDefinition INSTANCE;
}
}
diff --git a/glance/glance/api/restricted_current.txt b/glance/glance/api/restricted_current.txt
index 56e1253..5955625 100644
--- a/glance/glance/api/restricted_current.txt
+++ b/glance/glance/api/restricted_current.txt
@@ -117,12 +117,13 @@
method public static androidx.glance.action.MutableActionParameters mutableActionParametersOf(androidx.glance.action.ActionParameters.Pair<?>... pairs);
method public static androidx.glance.action.MutableActionParameters toMutableParameters(androidx.glance.action.ActionParameters);
method public static androidx.glance.action.ActionParameters toParameters(androidx.glance.action.ActionParameters);
+ method public static <T> androidx.glance.action.ActionParameters.Key<T> toParametersKey(androidx.datastore.preferences.core.Preferences.Key<T>);
}
public final class LaunchActivityActionKt {
- method public static androidx.glance.action.Action actionLaunchActivity(android.content.ComponentName componentName);
- method public static <T extends android.app.Activity> androidx.glance.action.Action actionLaunchActivity(Class<T> activity);
- method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action! actionLaunchActivity();
+ method public static androidx.glance.action.Action actionLaunchActivity(android.content.ComponentName componentName, optional androidx.glance.action.ActionParameters parameters);
+ method public static <T extends android.app.Activity> androidx.glance.action.Action actionLaunchActivity(Class<T> activity, optional androidx.glance.action.ActionParameters parameters);
+ method public static inline <reified T extends android.app.Activity> androidx.glance.action.Action! actionLaunchActivity(optional androidx.glance.action.ActionParameters parameters);
}
public final class MutableActionParameters extends androidx.glance.action.ActionParameters {
@@ -276,15 +277,15 @@
package androidx.glance.state {
- public final class GlanceState {
- method public suspend <T> Object? getValue(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, String fileName, kotlin.coroutines.Continuation<? super T> p);
- method public suspend <T> Object? updateValue(android.content.Context context, androidx.glance.state.GlanceStateDefinition<T> definition, String fileName, kotlin.jvm.functions.Function2<? super T,? super kotlin.coroutines.Continuation<? super T>,?> updateBlock, kotlin.coroutines.Continuation<? super T> p);
- field public static final androidx.glance.state.GlanceState INSTANCE;
+ public interface GlanceStateDefinition<T> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>> p);
+ method public java.io.File getLocation(android.content.Context context, String fileKey);
}
- public interface GlanceStateDefinition<T> {
- method public suspend <T> Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<T>> p);
+ public final class PreferencesGlanceStateDefinition implements androidx.glance.state.GlanceStateDefinition<androidx.datastore.preferences.core.Preferences> {
+ method public suspend Object? getDataStore(android.content.Context context, String fileKey, kotlin.coroutines.Continuation<? super androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences>> p);
method public java.io.File getLocation(android.content.Context context, String fileKey);
+ field public static final androidx.glance.state.PreferencesGlanceStateDefinition INSTANCE;
}
}
diff --git a/glance/glance/build.gradle b/glance/glance/build.gradle
index 1bd9e3b..1933c14 100644
--- a/glance/glance/build.gradle
+++ b/glance/glance/build.gradle
@@ -36,6 +36,8 @@
api("androidx.compose.ui:ui-graphics:1.1.0-beta01")
api("androidx.compose.ui:ui-unit:1.1.0-beta01")
api("androidx.datastore:datastore-core:1.0.0")
+ api("androidx.datastore:datastore-preferences-core:1.0.0")
+ api("androidx.datastore:datastore-preferences:1.0.0")
implementation("androidx.annotation:annotation:1.1.0")
implementation(libs.kotlinStdlib)
@@ -64,7 +66,6 @@
androidx {
name = "Glance Core Library"
type = LibraryType.PUBLISHED_LIBRARY
- publish = Publish.SNAPSHOT_ONLY
mavenGroup = LibraryGroups.GLANCE
inceptionYear = "2021"
description = "Glance allows developers to build layouts for remote surfaces using a Jetpack " +
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/action/ActionParameters.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/action/ActionParameters.kt
index 40ecf1d..eb02d71 100644
--- a/glance/glance/src/androidMain/kotlin/androidx/glance/action/ActionParameters.kt
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/action/ActionParameters.kt
@@ -16,6 +16,7 @@
package androidx.glance.action
+import androidx.datastore.preferences.core.Preferences
import java.util.Collections
/**
@@ -197,3 +198,7 @@
* @return a copy of this Parameters
*/
public fun ActionParameters.toParameters(): ActionParameters = toMutableParameters()
+
+/** Creates an action key from a preferences key. */
+public fun <T : Any> Preferences.Key<T>.toParametersKey(): ActionParameters.Key<T> =
+ ActionParameters.Key(name)
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/action/LaunchActivityAction.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/action/LaunchActivityAction.kt
index 387893d..ff1e90b 100644
--- a/glance/glance/src/androidMain/kotlin/androidx/glance/action/LaunchActivityAction.kt
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/action/LaunchActivityAction.kt
@@ -22,39 +22,55 @@
/** @suppress */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public interface LaunchActivityAction : Action
+public interface LaunchActivityAction : Action {
+ abstract val parameters: ActionParameters
+}
/** @suppress */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class LaunchActivityComponentAction(
- public val componentName: ComponentName
+ public val componentName: ComponentName,
+ public override val parameters: ActionParameters
) : LaunchActivityAction
/** @suppress */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class LaunchActivityClassAction(
- public val activityClass: Class<out Activity>
+ public val activityClass: Class<out Activity>,
+ public override val parameters: ActionParameters
) : LaunchActivityAction
/**
* Creates an [Action] that launches the [Activity] specified by the given [ComponentName].
*
* @param componentName component of the activity to launch
+ * @param parameters the parameters associated with the action. Parameter values will be added to
+ * the activity intent, keyed by the parameter key name string.
*/
-public fun actionLaunchActivity(componentName: ComponentName): Action =
- LaunchActivityComponentAction(componentName)
+public fun actionLaunchActivity(
+ componentName: ComponentName,
+ parameters: ActionParameters = actionParametersOf()
+): Action = LaunchActivityComponentAction(componentName, parameters)
/**
* Creates an [Action] that launches the specified [Activity] when triggered.
*
* @param activity class of the activity to launch
+ * @param parameters the parameters associated with the action. Parameter values will be added to
+ * the activity intent, keyed by the parameter key name string.
*/
-public fun <T : Activity> actionLaunchActivity(activity: Class<T>): Action =
- LaunchActivityClassAction(activity)
+public fun <T : Activity> actionLaunchActivity(
+ activity: Class<T>,
+ parameters: ActionParameters = actionParametersOf()
+): Action = LaunchActivityClassAction(activity, parameters)
@Suppress("MissingNullability") /* Shouldn't need to specify @NonNull. b/199284086 */
/**
* Creates an [Action] that launches the specified [Activity] when triggered.
+ *
+ * @param parameters the parameters associated with the action. Parameter values will be added to
+ * the activity intent, keyed by the parameter key name string.
*/
-public inline fun <reified T : Activity> actionLaunchActivity(): Action =
- actionLaunchActivity(T::class.java)
+public inline fun <reified T : Activity> actionLaunchActivity(
+ parameters: ActionParameters = actionParametersOf()
+): Action = actionLaunchActivity(T::class.java, parameters)
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/state/GlanceStateDefinition.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/state/GlanceStateDefinition.kt
index ca1cc33..51b9999 100644
--- a/glance/glance/src/androidMain/kotlin/androidx/glance/state/GlanceStateDefinition.kt
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/state/GlanceStateDefinition.kt
@@ -17,7 +17,11 @@
package androidx.glance.state
import android.content.Context
+import androidx.annotation.RestrictTo
import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.PreferenceDataStoreFactory
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.preferencesDataStoreFile
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -46,52 +50,69 @@
* @param fileKey The unique string key used to name and identify the data file corresponding to
* a remote UI. Each remote UI has a unique UI key, used to key the data for that UI.
*/
- public suspend fun <T> getDataStore(context: Context, fileKey: String): DataStore<T>
+ public suspend fun getDataStore(context: Context, fileKey: String): DataStore<T>
}
/**
* Data store for data specific to the glanceable view. Stored data should include information
* relevant to the representation of views, but not surface specific view data. For example, the
* month displayed on a calendar rather than actual calendar entries.
+ *
+ * @suppress
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public object GlanceState {
- // TODO(b/205496180): Make methods internal
/**
* Returns the stored data associated with the given UI key string.
*
* @param definition the configuration that defines this state.
- * @param fileName identifies the data file associated with the store, must be unique for any
+ * @param fileKey identifies the data file associated with the store, must be unique for any
* remote UI in the app.
*/
public suspend fun <T> getValue(
context: Context,
definition: GlanceStateDefinition<T>,
- fileName: String
- ): T = getDataStore(context, definition, fileName).data.first()
+ fileKey: String
+ ): T = getDataStore(context, definition, fileKey).data.first()
/**
* Updates the underlying data by applying the provided update block.
*
* @param definition the configuration that defines this state.
- * @param fileName identifies the date file associated with the store, must be unique for any
+ * @param fileKey identifies the date file associated with the store, must be unique for any
* remote UI in the app.
*/
public suspend fun <T> updateValue(
context: Context,
definition: GlanceStateDefinition<T>,
- fileName: String,
+ fileKey: String,
updateBlock: suspend (T) -> T
- ) = getDataStore(context, definition, fileName).updateData(updateBlock)
+ ): T = getDataStore(context, definition, fileKey).updateData(updateBlock)
+
+ /**
+ * Delete the file underlying the [DataStore] and remove local references to the [DataStore].
+ */
+ public suspend fun deleteStore(
+ context: Context,
+ definition: GlanceStateDefinition<*>,
+ fileKey: String
+ ) {
+ mutex.withLock {
+ dataStores.remove(fileKey)
+ val location = definition.getLocation(context, fileKey)
+ location.delete()
+ }
+ }
@Suppress("UNCHECKED_CAST")
private suspend fun <T> getDataStore(
context: Context,
definition: GlanceStateDefinition<T>,
- fileName: String
+ fileKey: String
): DataStore<T> =
mutex.withLock {
- dataStores.getOrPut(fileName) {
- definition.getDataStore<T>(context, fileName)
+ dataStores.getOrPut(fileKey) {
+ definition.getDataStore(context, fileKey)
} as DataStore<T>
}
@@ -100,3 +121,14 @@
// TODO: Move to a single, global source to manage the data lifecycle
private val dataStores: MutableMap<String, DataStore<*>> = mutableMapOf()
}
+
+/**
+ * Base class helping the creation of a state using DataStore's [Preferences].
+ */
+object PreferencesGlanceStateDefinition : GlanceStateDefinition<Preferences> {
+ override fun getLocation(context: Context, fileKey: String): File =
+ context.preferencesDataStoreFile(fileKey)
+
+ override suspend fun getDataStore(context: Context, fileKey: String): DataStore<Preferences> =
+ PreferenceDataStoreFactory.create { context.preferencesDataStoreFile(fileKey) }
+}
diff --git a/glance/glance/src/test/kotlin/androidx/glance/ButtonTest.kt b/glance/glance/src/test/kotlin/androidx/glance/ButtonTest.kt
index 6ae0921..6f49d6a2 100644
--- a/glance/glance/src/test/kotlin/androidx/glance/ButtonTest.kt
+++ b/glance/glance/src/test/kotlin/androidx/glance/ButtonTest.kt
@@ -17,8 +17,10 @@
import android.app.Activity
import androidx.glance.action.ActionModifier
+import androidx.glance.action.ActionParameters
import androidx.glance.action.LaunchActivityAction
import androidx.glance.action.actionLaunchActivity
+import androidx.glance.action.actionParametersOf
import androidx.glance.layout.runTestingComposition
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -39,15 +41,26 @@
@Test
fun createComposableButton() = fakeCoroutineScope.runBlockingTest {
+ val stringKey = ActionParameters.Key<String>("test")
+ val intKey = ActionParameters.Key<Int>("test2")
+ val string = "testString"
+ val int = 12
+
val root = runTestingComposition {
- Button(text = "button", onClick = actionLaunchActivity<Activity>(), enabled = true)
+ Button(text = "button", onClick = actionLaunchActivity<Activity>(
+ actionParametersOf(stringKey to string, intKey to int)
+ ), enabled = true)
}
assertThat(root.children).hasSize(1)
val child = assertIs<EmittableButton>(root.children[0])
assertThat(child.text).isEqualTo("button")
- assertIs<LaunchActivityAction>(child.modifier.findModifier<ActionModifier>()?.action)
+ val action =
+ assertIs<LaunchActivityAction>(child.modifier.findModifier<ActionModifier>()?.action)
assertThat(child.enabled).isTrue()
+ assertThat(action.parameters.asMap()).hasSize(2)
+ assertThat(action.parameters[stringKey]).isEqualTo(string)
+ assertThat(action.parameters[intKey]).isEqualTo(int)
}
@Test
diff --git a/glance/glance/src/test/kotlin/androidx/glance/state/StateDefinitionTest.kt b/glance/glance/src/test/kotlin/androidx/glance/state/StateDefinitionTest.kt
index 6610c26..91a054a 100644
--- a/glance/glance/src/test/kotlin/androidx/glance/state/StateDefinitionTest.kt
+++ b/glance/glance/src/test/kotlin/androidx/glance/state/StateDefinitionTest.kt
@@ -17,12 +17,8 @@
package androidx.glance.state
import android.content.Context
-import androidx.datastore.core.DataStore
-import androidx.datastore.preferences.core.PreferenceDataStoreFactory
-import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
-import androidx.datastore.preferences.preferencesDataStoreFile
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -30,7 +26,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
-import java.io.File
import kotlin.test.assertNotNull
@OptIn(ExperimentalCoroutinesApi::class)
@@ -45,7 +40,13 @@
fun emptyStore() {
runBlocking {
val uiString = "testUiString1"
- val store = assertNotNull(GlanceState.getValue(context, EmptyStateDefinition, uiString))
+ val store = assertNotNull(
+ GlanceState.getValue(
+ context,
+ PreferencesGlanceStateDefinition,
+ uiString
+ )
+ )
assertThat(store.contains(counterKey)).isFalse()
assertThat(store.contains(stringKey)).isFalse()
}
@@ -55,20 +56,32 @@
fun counterKey() {
runBlocking {
val uiString = "testUiString2"
- GlanceState.updateValue(context, EmptyStateDefinition, uiString) { prefs ->
+ GlanceState.updateValue(context, PreferencesGlanceStateDefinition, uiString) { prefs ->
prefs.toMutablePreferences().apply { this[counterKey] = 0 }.toPreferences()
}
- var store = assertNotNull(GlanceState.getValue(context, EmptyStateDefinition, uiString))
+ var store = assertNotNull(
+ GlanceState.getValue(
+ context,
+ PreferencesGlanceStateDefinition,
+ uiString
+ )
+ )
assertThat(store.contains(counterKey)).isTrue()
assertThat(store[counterKey]).isEqualTo(0)
- GlanceState.updateValue(context, EmptyStateDefinition, uiString) { prefs ->
+ GlanceState.updateValue(context, PreferencesGlanceStateDefinition, uiString) { prefs ->
prefs.toMutablePreferences().apply {
this[counterKey] = prefs[counterKey]!! + 1
}.toPreferences()
}
- store = assertNotNull(GlanceState.getValue(context, EmptyStateDefinition, uiString))
+ store = assertNotNull(
+ GlanceState.getValue(
+ context,
+ PreferencesGlanceStateDefinition,
+ uiString
+ )
+ )
assertThat(store.contains(counterKey)).isTrue()
assertThat(store[counterKey]).isEqualTo(1)
}
@@ -81,36 +94,37 @@
val storedMessage2 = "Another test string"
runBlocking {
- GlanceState.updateValue(context, EmptyStateDefinition, uiString) { prefs ->
+ GlanceState.updateValue(context, PreferencesGlanceStateDefinition, uiString) { prefs ->
prefs.toMutablePreferences().apply { this[stringKey] = storedMessage1 }
.toPreferences()
}
- var store = assertNotNull(GlanceState.getValue(context, EmptyStateDefinition, uiString))
+ var store = assertNotNull(
+ GlanceState.getValue(
+ context,
+ PreferencesGlanceStateDefinition,
+ uiString
+ )
+ )
assertThat(store.contains(counterKey)).isFalse()
assertThat(store.contains(stringKey)).isTrue()
assertThat(store[stringKey]).isEqualTo(storedMessage1)
- GlanceState.updateValue(context, EmptyStateDefinition, uiString) { prefs ->
+ GlanceState.updateValue(context, PreferencesGlanceStateDefinition, uiString) { prefs ->
prefs.toMutablePreferences().apply {
this[stringKey] = storedMessage2
}.toPreferences()
}
- store = assertNotNull(GlanceState.getValue(context, EmptyStateDefinition, uiString))
+ store = assertNotNull(
+ GlanceState.getValue(
+ context,
+ PreferencesGlanceStateDefinition,
+ uiString
+ )
+ )
assertThat(store.contains(counterKey)).isFalse()
assertThat(store.contains(stringKey)).isTrue()
assertThat(store[stringKey]).isEqualTo(storedMessage2)
}
}
-
- object EmptyStateDefinition : GlanceStateDefinition<Preferences> {
- override fun getLocation(context: Context, fileKey: String): File =
- context.preferencesDataStoreFile(fileKey)
-
- @Suppress("UNCHECKED_CAST")
- override suspend fun <T> getDataStore(context: Context, fileKey: String): DataStore<T> =
- PreferenceDataStoreFactory.create {
- context.preferencesDataStoreFile(fileKey)
- } as DataStore<T>
- }
}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 1f962fa..c44b094 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,4 @@
-org.gradle.jvmargs=-Xmx8g -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -Dkotlin.daemon.jvm.options=-XX:MaxMetaspaceSize=1g
+org.gradle.jvmargs=-Xmx8g -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -Dkotlin.daemon.jvm.options=-XX:MaxMetaspaceSize=1g -Dlint.nullness.ignore-deprecated=true
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.parallel=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index eb02229..6bcb125 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
androidLintMinCompose = "30.0.0"
androidxTestRunner = "1.4.0"
androidxTestRules = "1.4.0"
-androidxTestMonitor = "1.5.0-beta01"
+androidxTestMonitor = "1.5.0-rc01"
androidxTestCore = "1.4.0"
androidxTestExtJunit = "1.1.3"
androidxTestExtTruth = "1.4.0"
@@ -145,7 +145,7 @@
protobufLite = { module = "com.google.protobuf:protobuf-javalite", version = "3.10.0" }
reactiveStreams = { module = "org.reactivestreams:reactive-streams", version = "1.0.0" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version = "2.7.2" }
-robolectric = { module = "org.robolectric:robolectric", version = "4.6.1" }
+robolectric = { module = "org.robolectric:robolectric", version = "4.7" }
rxjava2 = { module = "io.reactivex.rxjava2:rxjava", version = "2.2.9" }
rxjava3 = { module = "io.reactivex.rxjava3:rxjava", version = "3.0.0" }
shadow = { module = "gradle.plugin.com.github.johnrengelman:shadow", version = "7.1.0" }
@@ -173,3 +173,6 @@
wireRuntime = { module = "com.squareup.wire:wire-runtime", version.ref = "wire" }
xpp3 = { module = "xpp3:xpp3", version = "1.1.4c" }
xmlpull = { module = "xmlpull:xmlpull", version = "1.1.3.1" }
+
+[plugins]
+kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 7f930a72..e7769e2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,3 @@
-#Tue Aug 16 10:43:36 PDT 2016
-# IMPORTANT: Also update ui/gradle/wrapper/gradle-wrapper.properties
-# when changing this file
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
index de040cf..50e6f8d 100644
--- a/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
+++ b/inspection/inspection-gradle-plugin/src/main/kotlin/androidx/inspection/gradle/DexInspectorTask.kt
@@ -25,6 +25,7 @@
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
@@ -42,6 +43,7 @@
import java.io.File
import java.nio.charset.Charset
+@CacheableTask
abstract class DexInspectorTask : DefaultTask() {
@get:PathSensitive(PathSensitivity.NONE)
@get:InputFile
diff --git a/jetifier/OWNERS b/jetifier/OWNERS
deleted file mode 100644
index 8ae348f..0000000
--- a/jetifier/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-pavlis@google.com
diff --git a/lifecycle/OWNERS b/lifecycle/OWNERS
index 99fbeef..27789fd 100644
--- a/lifecycle/OWNERS
+++ b/lifecycle/OWNERS
@@ -1,7 +1,9 @@
# Bug component: 413132
+ilake@google.com
+jbwoods@google.com
+sanura@google.com
sergeyv@google.com
yboyar@google.com
-jbwoods@google.com
per-file settings.gradle = dustinlam@google.com, rahulrav@google.com
diff --git a/lifecycle/lifecycle-runtime-ktx-lint/build.gradle b/lifecycle/lifecycle-runtime-ktx-lint/build.gradle
index f0497c3..8776e4f 100644
--- a/lifecycle/lifecycle-runtime-ktx-lint/build.gradle
+++ b/lifecycle/lifecycle-runtime-ktx-lint/build.gradle
@@ -28,6 +28,8 @@
compileOnly(libs.kotlinStdlib)
testImplementation(libs.kotlinStdlib)
+ testImplementation(libs.kotlinReflect)
+ testImplementation(libs.kotlinStdlibJdk8)
testImplementation(libs.androidLint)
testImplementation(libs.androidLintTests)
testImplementation(libs.junit)
diff --git a/lifecycle/lifecycle-runtime-ktx-lint/src/main/java/androidx/lifecycle/lint/LifecycleWhenChecks.kt b/lifecycle/lifecycle-runtime-ktx-lint/src/main/java/androidx/lifecycle/lint/LifecycleWhenChecks.kt
index a1d8509..f863f3c 100644
--- a/lifecycle/lifecycle-runtime-ktx-lint/src/main/java/androidx/lifecycle/lint/LifecycleWhenChecks.kt
+++ b/lifecycle/lifecycle-runtime-ktx-lint/src/main/java/androidx/lifecycle/lint/LifecycleWhenChecks.kt
@@ -42,7 +42,9 @@
import org.jetbrains.uast.UIfExpression
import org.jetbrains.uast.ULambdaExpression
import org.jetbrains.uast.UMethod
+import org.jetbrains.uast.USwitchClauseExpression
import org.jetbrains.uast.UTryExpression
+import org.jetbrains.uast.kotlin.KotlinUSwitchEntry
import org.jetbrains.uast.toUElement
import org.jetbrains.uast.tryResolve
import org.jetbrains.uast.visitor.AbstractUastVisitor
@@ -190,6 +192,23 @@
}
return false
}
+
+ override fun visitSwitchClauseExpression(node: USwitchClauseExpression): Boolean {
+ // check each case in the switch statement
+ node.caseValues.forEach { expression ->
+ val method = expression.tryResolve() as? PsiMethod ?: return false
+ if (method.isLifecycleIsAtLeastMethod(context)) {
+ // If the case containing the lifecycle check evaluates to true, check the body
+ withNewState(checkUIAccess = false) {
+ (node as? KotlinUSwitchEntry)?.body?.expressions?.forEach {
+ it.accept(this)
+ }
+ }
+ return true
+ }
+ }
+ return false
+ }
}
private const val DISPATCHER_CLASS_NAME = "androidx.lifecycle.PausingDispatcherKt"
diff --git a/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/LifecycleWhenChecksTest.kt b/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/LifecycleWhenChecksTest.kt
index dbba1ac..e45da05 100644
--- a/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/LifecycleWhenChecksTest.kt
+++ b/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/LifecycleWhenChecksTest.kt
@@ -24,7 +24,6 @@
import com.android.tools.lint.checks.infrastructure.TestFiles.kt
import com.android.tools.lint.checks.infrastructure.TestLintResult
import com.android.tools.lint.checks.infrastructure.TestLintTask
-import com.android.tools.lint.checks.infrastructure.TestMode
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -35,8 +34,6 @@
private fun check(body: String): TestLintResult {
return TestLintTask.lint()
.files(VIEW_STUB, LIFECYCLE_STUB, COROUTINES_STUB, kt(template(body)))
- .allowCompilationErrors(true) // b/193267317
- .skipTestModes(TestMode.IF_TO_WHEN) // b/203245752
.issues(ISSUE)
.run()
}
diff --git a/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/RepeatOnLifecycleDetectorTest.kt b/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/RepeatOnLifecycleDetectorTest.kt
index cce75cd..aa6e8ef 100644
--- a/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/RepeatOnLifecycleDetectorTest.kt
+++ b/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/RepeatOnLifecycleDetectorTest.kt
@@ -107,7 +107,6 @@
*REPEAT_ON_LIFECYCLE_STUBS,
TestFiles.kt(fileToAdd)
)
- .allowCompilationErrors(true) // b/193267317
.issues(RepeatOnLifecycleDetector.ISSUE)
.run()
}
diff --git a/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/WhenMethodsTest.kt b/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/WhenMethodsTest.kt
index 5b26766..701f1da 100644
--- a/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/WhenMethodsTest.kt
+++ b/lifecycle/lifecycle-runtime-ktx-lint/src/test/java/androidx/lifecycle/runtime/lint/WhenMethodsTest.kt
@@ -51,7 +51,6 @@
private fun check(body: String): TestLintResult {
return TestLintTask.lint()
- .allowCompilationErrors(true) // b/193267317
.files(VIEW_STUB, LIFECYCLE_STUB, COROUTINES_STUB, TestFiles.kt(template(body)))
.issues(LifecycleWhenChecks.ISSUE)
.run()
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index 737c820..5075f11 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -37,6 +37,9 @@
BanInappropriateExperimentalUsage.ISSUE,
BanKeepAnnotation.ISSUE,
TargetApiAnnotationUsageDetector.ISSUE,
+ // If you add more SampledAnnotationDetector issues here, you
+ // MUST also update `buildSrc/lint_samples.xml` to ensure they
+ // run against samples projects.
SampledAnnotationDetector.OBSOLETE_SAMPLED_ANNOTATION,
SampledAnnotationDetector.UNRESOLVED_SAMPLE_LINK,
SampledAnnotationDetector.MULTIPLE_FUNCTIONS_FOUND,
diff --git a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java
index 8bde7c9..c950d58 100644
--- a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java
+++ b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/RemoteUserInfoWithMediaControllerCompatTest.java
@@ -46,6 +46,7 @@
import android.view.KeyEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import org.junit.After;
@@ -61,6 +62,7 @@
* Test of {@link MediaSessionCompat#getCurrentControllerInfo()} with all
* {@link MediaControllerCompat} methods.
*/
+@FlakyTest(bugId = 206969269)
@RunWith(AndroidJUnit4.class)
@LargeTest
public class RemoteUserInfoWithMediaControllerCompatTest {
diff --git a/mediarouter/mediarouter/api/current.txt b/mediarouter/mediarouter/api/current.txt
index 6af2727..a750177 100644
--- a/mediarouter/mediarouter/api/current.txt
+++ b/mediarouter/mediarouter/api/current.txt
@@ -452,7 +452,7 @@
public class MediaRouterParams {
method public int getDialogType();
- method public boolean isMediaTransferReceiverDisabled();
+ method public boolean isMediaTransferReceiverEnabled();
method public boolean isOutputSwitcherEnabled();
method public boolean isTransferToLocalEnabled();
field public static final int DIALOG_TYPE_DEFAULT = 1; // 0x1
@@ -465,7 +465,7 @@
ctor public MediaRouterParams.Builder(androidx.mediarouter.media.MediaRouterParams);
method public androidx.mediarouter.media.MediaRouterParams build();
method public androidx.mediarouter.media.MediaRouterParams.Builder setDialogType(int);
- method public androidx.mediarouter.media.MediaRouterParams.Builder setMediaTransferReceiverDisabled(boolean);
+ method public androidx.mediarouter.media.MediaRouterParams.Builder setMediaTransferReceiverEnabled(boolean);
method public androidx.mediarouter.media.MediaRouterParams.Builder setOutputSwitcherEnabled(boolean);
method public androidx.mediarouter.media.MediaRouterParams.Builder setTransferToLocalEnabled(boolean);
}
diff --git a/mediarouter/mediarouter/api/public_plus_experimental_current.txt b/mediarouter/mediarouter/api/public_plus_experimental_current.txt
index 6af2727..a750177 100644
--- a/mediarouter/mediarouter/api/public_plus_experimental_current.txt
+++ b/mediarouter/mediarouter/api/public_plus_experimental_current.txt
@@ -452,7 +452,7 @@
public class MediaRouterParams {
method public int getDialogType();
- method public boolean isMediaTransferReceiverDisabled();
+ method public boolean isMediaTransferReceiverEnabled();
method public boolean isOutputSwitcherEnabled();
method public boolean isTransferToLocalEnabled();
field public static final int DIALOG_TYPE_DEFAULT = 1; // 0x1
@@ -465,7 +465,7 @@
ctor public MediaRouterParams.Builder(androidx.mediarouter.media.MediaRouterParams);
method public androidx.mediarouter.media.MediaRouterParams build();
method public androidx.mediarouter.media.MediaRouterParams.Builder setDialogType(int);
- method public androidx.mediarouter.media.MediaRouterParams.Builder setMediaTransferReceiverDisabled(boolean);
+ method public androidx.mediarouter.media.MediaRouterParams.Builder setMediaTransferReceiverEnabled(boolean);
method public androidx.mediarouter.media.MediaRouterParams.Builder setOutputSwitcherEnabled(boolean);
method public androidx.mediarouter.media.MediaRouterParams.Builder setTransferToLocalEnabled(boolean);
}
diff --git a/mediarouter/mediarouter/api/restricted_current.txt b/mediarouter/mediarouter/api/restricted_current.txt
index 6af2727..a750177 100644
--- a/mediarouter/mediarouter/api/restricted_current.txt
+++ b/mediarouter/mediarouter/api/restricted_current.txt
@@ -452,7 +452,7 @@
public class MediaRouterParams {
method public int getDialogType();
- method public boolean isMediaTransferReceiverDisabled();
+ method public boolean isMediaTransferReceiverEnabled();
method public boolean isOutputSwitcherEnabled();
method public boolean isTransferToLocalEnabled();
field public static final int DIALOG_TYPE_DEFAULT = 1; // 0x1
@@ -465,7 +465,7 @@
ctor public MediaRouterParams.Builder(androidx.mediarouter.media.MediaRouterParams);
method public androidx.mediarouter.media.MediaRouterParams build();
method public androidx.mediarouter.media.MediaRouterParams.Builder setDialogType(int);
- method public androidx.mediarouter.media.MediaRouterParams.Builder setMediaTransferReceiverDisabled(boolean);
+ method public androidx.mediarouter.media.MediaRouterParams.Builder setMediaTransferReceiverEnabled(boolean);
method public androidx.mediarouter.media.MediaRouterParams.Builder setOutputSwitcherEnabled(boolean);
method public androidx.mediarouter.media.MediaRouterParams.Builder setTransferToLocalEnabled(boolean);
}
diff --git a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterTest.java b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterTest.java
index aa9fff3..186093b 100644
--- a/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterTest.java
+++ b/mediarouter/mediarouter/src/androidTest/java/androidx/mediarouter/media/MediaRouterTest.java
@@ -130,7 +130,7 @@
final int dialogType = MediaRouterParams.DIALOG_TYPE_DYNAMIC_GROUP;
final boolean isOutputSwitcherEnabled = true;
final boolean transferToLocalEnabled = true;
- final boolean transferReceiverDisabled = true;
+ final boolean transferReceiverEnabled = false;
final Bundle extras = new Bundle();
extras.putString(TEST_KEY, TEST_VALUE);
@@ -138,7 +138,7 @@
.setDialogType(dialogType)
.setOutputSwitcherEnabled(isOutputSwitcherEnabled)
.setTransferToLocalEnabled(transferToLocalEnabled)
- .setMediaTransferReceiverDisabled(transferReceiverDisabled)
+ .setMediaTransferReceiverEnabled(transferReceiverEnabled)
.setExtras(extras)
.build();
@@ -147,13 +147,13 @@
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
assertEquals(isOutputSwitcherEnabled, params.isOutputSwitcherEnabled());
assertEquals(transferToLocalEnabled, params.isTransferToLocalEnabled());
- assertEquals(transferReceiverDisabled, params.isMediaTransferReceiverDisabled());
+ assertEquals(transferReceiverEnabled, params.isMediaTransferReceiverEnabled());
} else {
// Earlier than Android R, output switcher cannot be enabled.
// Same for transfer to local.
assertFalse(params.isOutputSwitcherEnabled());
assertFalse(params.isTransferToLocalEnabled());
- assertFalse(params.isMediaTransferReceiverDisabled());
+ assertFalse(params.isMediaTransferReceiverEnabled());
}
extras.remove(TEST_KEY);
@@ -164,8 +164,8 @@
assertEquals(params.getDialogType(), copiedParams.getDialogType());
assertEquals(params.isOutputSwitcherEnabled(), copiedParams.isOutputSwitcherEnabled());
assertEquals(params.isTransferToLocalEnabled(), copiedParams.isTransferToLocalEnabled());
- assertEquals(params.isMediaTransferReceiverDisabled(),
- copiedParams.isMediaTransferReceiverDisabled());
+ assertEquals(params.isMediaTransferReceiverEnabled(),
+ copiedParams.isMediaTransferReceiverEnabled());
assertBundleEquals(params.getExtras(), copiedParams.getExtras());
}
@@ -176,7 +176,7 @@
final int dialogType = MediaRouterParams.DIALOG_TYPE_DYNAMIC_GROUP;
final boolean isOutputSwitcherEnabled = true;
final boolean transferToLocalEnabled = true;
- final boolean transferReceiverDisabled = true;
+ final boolean transferReceiverEnabled = false;
final Bundle paramExtras = new Bundle();
paramExtras.putString(TEST_KEY, TEST_VALUE);
@@ -184,7 +184,7 @@
.setDialogType(dialogType)
.setOutputSwitcherEnabled(isOutputSwitcherEnabled)
.setTransferToLocalEnabled(transferToLocalEnabled)
- .setMediaTransferReceiverDisabled(transferReceiverDisabled)
+ .setMediaTransferReceiverEnabled(transferReceiverEnabled)
.setExtras(paramExtras)
.build();
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
index 6eb9f22..3334211 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
@@ -2654,6 +2654,8 @@
mMr2Provider = new MediaRoute2Provider(
mApplicationContext, new Mr2ProviderCallback());
addProvider(mMr2Provider);
+ // Make sure mDiscoveryRequestForMr2Provider is updated
+ updateDiscoveryRequest();
mRegisteredProviderWatcher.rescan();
}
@@ -2938,8 +2940,9 @@
}
boolean isMediaTransferEnabled() {
+ // The default value for isMediaTransferReceiverEnabled() is {@code true}.
return mTransferReceiverDeclared
- && (mRouterParams == null || !mRouterParams.isMediaTransferReceiverDisabled());
+ && (mRouterParams == null || mRouterParams.isMediaTransferReceiverEnabled());
}
boolean isTransferToLocalEnabled() {
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterParams.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterParams.java
index f580da1..405202f 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterParams.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouterParams.java
@@ -76,14 +76,14 @@
@DialogType
final int mDialogType;
- final boolean mMediaTransferReceiverDisabled;
+ final boolean mMediaTransferReceiverEnabled;
final boolean mOutputSwitcherEnabled;
final boolean mTransferToLocalEnabled;
final Bundle mExtras;
MediaRouterParams(@NonNull Builder builder) {
mDialogType = builder.mDialogType;
- mMediaTransferReceiverDisabled = builder.mMediaTransferDisabled;
+ mMediaTransferReceiverEnabled = builder.mMediaTransferEnabled;
mOutputSwitcherEnabled = builder.mOutputSwitcherEnabled;
mTransferToLocalEnabled = builder.mTransferToLocalEnabled;
@@ -101,12 +101,12 @@
}
/**
- * Gets whether declared {@link MediaTransferReceiver} is disabled.
+ * Gets whether declared {@link MediaTransferReceiver} is enabled.
*
- * @see Builder#setMediaTransferReceiverDisabled(boolean)
+ * @see Builder#setMediaTransferReceiverEnabled(boolean)
*/
- public boolean isMediaTransferReceiverDisabled() {
- return mMediaTransferReceiverDisabled;
+ public boolean isMediaTransferReceiverEnabled() {
+ return mMediaTransferReceiverEnabled;
}
/**
@@ -146,7 +146,7 @@
public static final class Builder {
@DialogType
int mDialogType = DIALOG_TYPE_DEFAULT;
- boolean mMediaTransferDisabled;
+ boolean mMediaTransferEnabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
boolean mOutputSwitcherEnabled;
boolean mTransferToLocalEnabled;
Bundle mExtras;
@@ -170,7 +170,7 @@
mDialogType = params.mDialogType;
mOutputSwitcherEnabled = params.mOutputSwitcherEnabled;
mTransferToLocalEnabled = params.mTransferToLocalEnabled;
- mMediaTransferDisabled = params.mMediaTransferReceiverDisabled;
+ mMediaTransferEnabled = params.mMediaTransferReceiverEnabled;
mExtras = params.mExtras == null ? null : new Bundle(params.mExtras);
}
@@ -194,12 +194,13 @@
}
/**
- * Sets whether declared {@link MediaTransferReceiver} is disabled. This method will be
- * no-op for Android versions earlier than Android R. The default value is {@code false}.
+ * Sets whether declared {@link MediaTransferReceiver} is enabled. The default value is
+ * {@code true}. This method will be no-op for Android versions earlier than Android R and
+ * it stays {@code false} on devices earlier than Android R.
* <p>
- * It is used to disable media transfer feature when {@link MediaTransferReceiver} is
+ * It can be used to disable media transfer feature when {@link MediaTransferReceiver} is
* declared.
- * If set to {@code true}, media transfer feature will be disabled
+ * If set to {@code false}, media transfer feature will be disabled
* even when {@link MediaTransferReceiver} is declared.
* <p>
* It is not recommended to change this value at runtime.
@@ -207,9 +208,9 @@
* @see MediaTransferReceiver
*/
@NonNull
- public Builder setMediaTransferReceiverDisabled(boolean disabled) {
+ public Builder setMediaTransferReceiverEnabled(boolean enabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- mMediaTransferDisabled = disabled;
+ mMediaTransferEnabled = enabled;
}
return this;
}
diff --git a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
index 59d3623..be66cde 100644
--- a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
+++ b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
@@ -714,6 +714,36 @@
}
@Test
+ fun testGetDialogViewModel() {
+ lateinit var navController: NavHostController
+ lateinit var model: TestViewModel
+
+ composeTestRule.setContent {
+ navController = rememberNavController()
+ NavHost(navController, first) {
+ composable(first) { }
+ dialog(second) {
+ model = viewModel(it)
+ }
+ }
+ }
+
+ composeTestRule.runOnIdle {
+ navController.navigate(second)
+ }
+
+ composeTestRule.runOnIdle {
+ navController.popBackStack()
+ }
+
+ assertThat(model.wasCleared).isFalse()
+
+ composeTestRule.waitForIdle()
+
+ assertThat(model.wasCleared).isTrue()
+ }
+
+ @Test
fun testGetGraphViewModelAfterRecompose() {
lateinit var navController: NavHostController
lateinit var model: TestViewModel
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogHost.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogHost.kt
index c8ef92c..700efea 100644
--- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogHost.kt
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogHost.kt
@@ -17,6 +17,7 @@
package androidx.navigation.compose
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
@@ -47,6 +48,12 @@
backStackEntry.LocalOwnersProvider(saveableStateHolder) {
destination.content(backStackEntry)
}
+
+ DisposableEffect(backStackEntry) {
+ onDispose {
+ dialogNavigator.onTransitionComplete(backStackEntry)
+ }
+ }
}
}
}
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogNavigator.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogNavigator.kt
index 4d433ec..bfac256 100644
--- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogNavigator.kt
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogNavigator.kt
@@ -61,7 +61,11 @@
}
override fun popBackStack(popUpTo: NavBackStackEntry, savedState: Boolean) {
- state.pop(popUpTo, savedState)
+ state.popWithTransition(popUpTo, savedState)
+ }
+
+ internal fun onTransitionComplete(entry: NavBackStackEntry) {
+ state.markTransitionComplete(entry)
}
/**
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorTest.kt
index c1e144d..fb24f725 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorTest.kt
@@ -302,6 +302,82 @@
}
@Test
+ fun testEquals() {
+ val firstDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ setComponentName(ComponentName(activityRule.activity, TargetActivity::class.java))
+ }
+ val secondDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ setComponentName(ComponentName(activityRule.activity, TargetActivity::class.java))
+ }
+ assertThat(firstDestination).isEqualTo(secondDestination)
+ }
+
+ @Test
+ fun testFilterEquals() {
+ val firstDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ setComponentName(ComponentName(activityRule.activity, TargetActivity::class.java))
+ }
+ val secondDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ setComponentName(ComponentName(activityRule.activity, TargetActivity::class.java))
+ intent!!.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ assertThat(firstDestination).isEqualTo(secondDestination)
+ }
+
+ @Test
+ fun testEqualsBothIntentNull() {
+ val firstDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ }
+ val secondDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ }
+ assertThat(firstDestination).isEqualTo(secondDestination)
+ }
+
+ @Test
+ fun testNotEquals() {
+ val firstDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ setComponentName(ComponentName(activityRule.activity, TargetActivity::class.java))
+ }
+ val secondDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ setComponentName(ComponentName(activityRule.activity, TargetActivity::class.java))
+ setAction(TARGET_ACTION)
+ }
+ assertThat(firstDestination).isNotEqualTo(secondDestination)
+ }
+
+ @Test
+ fun testNotEqualsFirstIntentNull() {
+ val firstDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ }
+ val secondDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ setComponentName(ComponentName(activityRule.activity, TargetActivity::class.java))
+ }
+ assertThat(firstDestination).isNotEqualTo(secondDestination)
+ }
+
+ @Test
+ fun testNotEqualsSecondIntentNull() {
+ val firstDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ setComponentName(ComponentName(activityRule.activity, TargetActivity::class.java))
+ }
+ val secondDestination = activityNavigator.createDestination().apply {
+ id = TARGET_ID
+ }
+ assertThat(firstDestination).isNotEqualTo(secondDestination)
+ }
+
+ @Test
fun testToString() {
val targetDestination = activityNavigator.createDestination().apply {
id = TARGET_ID
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.kt
index f52061b..8d880eac 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.kt
@@ -407,22 +407,14 @@
override fun equals(other: Any?): Boolean {
if (other == null || other !is Destination) return false
return super.equals(other) &&
- intent == other.intent &&
- dataPattern == other.dataPattern &&
- targetPackage == other.targetPackage &&
- component == other.component &&
- action == other.action &&
- data == other.data
+ intent?.filterEquals(other.intent) ?: (other.intent == null) &&
+ dataPattern == other.dataPattern
}
override fun hashCode(): Int {
var result = super.hashCode()
- result = 31 * result + intent.hashCode()
+ result = 31 * result + (intent?.filterHashCode() ?: 0)
result = 31 * result + dataPattern.hashCode()
- result = 31 * result + targetPackage.hashCode()
- result = 31 * result + component.hashCode()
- result = 31 * result + action.hashCode()
- result = 31 * result + data.hashCode()
return result
}
}
diff --git a/paging/paging-common/src/test/kotlin/androidx/paging/CachedPageEventFlowLeakTest.kt b/paging/paging-common/src/test/kotlin/androidx/paging/CachedPageEventFlowLeakTest.kt
index d38b63f..415c3a4 100644
--- a/paging/paging-common/src/test/kotlin/androidx/paging/CachedPageEventFlowLeakTest.kt
+++ b/paging/paging-common/src/test/kotlin/androidx/paging/CachedPageEventFlowLeakTest.kt
@@ -26,6 +26,7 @@
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@@ -196,6 +197,8 @@
}
}
+ // Broken: b/206981029
+ @Ignore
@Test
public fun dontLeakPreviousPageInfoWithCache_stillCollecting() {
val scope = CoroutineScope(EmptyCoroutineContext)
diff --git a/playground-common/androidx-shared.properties b/playground-common/androidx-shared.properties
index 335fef47..958efb4 100644
--- a/playground-common/androidx-shared.properties
+++ b/playground-common/androidx-shared.properties
@@ -24,7 +24,7 @@
# This separation is necessary to ensure gradle can read certain properties
# at configuration time.
-org.gradle.jvmargs=-Xmx8g -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -Dkotlin.daemon.jvm.options=-XX:MaxMetaspaceSize=1g
+org.gradle.jvmargs=-Xmx8g -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -Dkotlin.daemon.jvm.options=-XX:MaxMetaspaceSize=1g -Dlint.nullness.ignore-deprecated=true
android.useAndroidX=true
# Disable features we do not use
android.defaults.buildfeatures.aidl=false
diff --git a/playground-common/playground-plugin/build.gradle b/playground-common/playground-plugin/build.gradle
index 9fff29a..7b521cf 100644
--- a/playground-common/playground-plugin/build.gradle
+++ b/playground-common/playground-plugin/build.gradle
@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- plugins {
+
+plugins {
id "groovy-gradle-plugin"
+ alias(libs.plugins.kotlinJvm)
}
repositories {
@@ -26,6 +28,15 @@
implementation "com.gradle:common-custom-user-data-gradle-plugin:1.5"
}
+// Needed to be able to use Groovy code from Kotlin
+// https://docs.gradle.org/6.1-rc-1/release-notes.html#compilation-order
+tasks.named('compileGroovy') {
+ classpath = sourceSets.main.compileClasspath
+}
+tasks.named('compileKotlin') {
+ classpath += files(sourceSets.main.groovy.classesDirectory)
+}
+
gradlePlugin {
plugins {
playgroundConventions {
diff --git a/playground-common/playground-plugin/settings.gradle b/playground-common/playground-plugin/settings.gradle
index 9c18267..cb8499c 100644
--- a/playground-common/playground-plugin/settings.gradle
+++ b/playground-common/playground-plugin/settings.gradle
@@ -33,3 +33,12 @@
}
}
}
+
+enableFeaturePreview("VERSION_CATALOGS")
+dependencyResolutionManagement {
+ versionCatalogs {
+ libs {
+ from(files("../../gradle/libs.versions.toml"))
+ }
+ }
+}
\ No newline at end of file
diff --git a/playground-common/playground-plugin/src/main/groovy/androidx/playground/GradleEnterpriseConventionsPlugin.groovy b/playground-common/playground-plugin/src/main/groovy/androidx/playground/GradleEnterpriseConventionsPlugin.groovy
deleted file mode 100644
index 566904b..0000000
--- a/playground-common/playground-plugin/src/main/groovy/androidx/playground/GradleEnterpriseConventionsPlugin.groovy
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package androidx.playground
-
-import org.gradle.api.Plugin
-import org.gradle.api.initialization.Settings
-import org.gradle.caching.http.HttpBuildCache
-
-class GradleEnterpriseConventionsPlugin implements Plugin<Settings> {
- void apply(Settings settings) {
- settings.apply(plugin: "com.gradle.enterprise")
- settings.apply(plugin: "com.gradle.common-custom-user-data-gradle-plugin")
-
- // Github Actions always sets a "CI" environment variable
- var isCI = System.getenv("CI") != null
-
- settings.gradleEnterprise {
- server = "https://ge.androidx.dev"
-
- buildScan {
- publishAlways()
- publishIfAuthenticated()
-
- uploadInBackground = !isCI
-
- capture {
- taskInputFiles = true
- }
- obfuscation {
- hostname { host -> "unset" }
- ipAddresses { addresses -> addresses.collect { address -> "0.0.0.0"} }
- }
- }
- }
-
- settings.buildCache {
- remote(HttpBuildCache) {
- url = "https://ge.androidx.dev/cache/"
- var buildCachePassword = System.getenv("GRADLE_BUILD_CACHE_PASSWORD")
- if (isCI && buildCachePassword != null && !buildCachePassword.empty) {
- push = true
- credentials {
- username = "ci"
- password = buildCachePassword
- }
- } else {
- push = false
- }
- }
- }
- }
-}
diff --git a/playground-common/playground-plugin/src/main/groovy/androidx/playground/GradleWorkaround.groovy b/playground-common/playground-plugin/src/main/groovy/androidx/playground/GradleWorkaround.groovy
new file mode 100644
index 0000000..d85061c
--- /dev/null
+++ b/playground-common/playground-plugin/src/main/groovy/androidx/playground/GradleWorkaround.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.playground
+
+import com.gradle.scan.plugin.BuildScanDataObfuscation
+
+class GradleWorkaround {
+ /**
+ * This is needed because Gradle configuration caching fails to serialize these lambdas
+ * if they are written in Kotlin
+ * https://github.com/gradle/gradle/issues/19047
+ */
+ static void obfuscate(BuildScanDataObfuscation obfuscation) {
+ obfuscation.hostname {"unset" }
+ obfuscation.ipAddresses { addresses -> addresses.collect { address -> "0.0.0.0"} }
+ }
+}
\ No newline at end of file
diff --git a/playground-common/playground-plugin/src/main/groovy/androidx/playground/PlaygroundExtension.groovy b/playground-common/playground-plugin/src/main/groovy/androidx/playground/PlaygroundExtension.groovy
deleted file mode 100644
index ab9daac..0000000
--- a/playground-common/playground-plugin/src/main/groovy/androidx/playground/PlaygroundExtension.groovy
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package androidx.playground
-
-import javax.inject.Inject
-import org.gradle.api.GradleException
-import org.gradle.api.model.ObjectFactory
-import org.gradle.api.initialization.Settings
-
-class PlaygroundExtension {
- private final ObjectFactory objectFactory
- private final Settings settings
-
- private File supportRootDir
-
- @Inject
- PlaygroundExtension(Settings settings, ObjectFactory objectFactory) {
- this.settings = settings
- this.objectFactory = objectFactory
- }
-
- /**
- * Includes the project if it does not already exist.
- * This is invoked from `includeProject` to ensure all parent projects are included. If they are
- * not, gradle will use the root project path to set the projectDir, which might conflict in
- * playground. Instead, this method checks if another project in that path exists and if so,
- * changes the project dir to avoid the conflict.
- * see b/197253160 for details.
- */
- private includeFakeParentProjectIfNotExists(String name, File projectDir) {
- if (name.isEmpty()) return
- if (settings.findProject(name)) {
- return
- }
- if (settings.findProject(projectDir) != null) {
- // Project directory conflicts with an existing project (possibly root). Move it
- // to another directory to avoid the conflict.
- projectDir = new File(projectDir.getParentFile(), ".ignore-${projectDir.name}")
- }
- includeProjectAt(name, projectDir)
- // Set it to a gradle file that does not exist.
- // We must always include projects starting with root, if we are including nested projects.
- settings.project(name).buildFileName = "ignored.gradle"
- }
-
- private includeProjectAt(String name, File projectDir) {
- if (settings.findProject(name) != null) {
- throw new GradleException("Cannot include project twice: $name is already included.")
- }
- def parentPath = name.substring(0, name.lastIndexOf(":"))
- def parentDir = projectDir.getParentFile()
- // Make sure parent is created first. see: b/197253160 for details
- includeFakeParentProjectIfNotExists(
- parentPath,
- parentDir
- )
- settings.include(name)
- settings.project(name).projectDir = projectDir
- }
-
- /**
- * Includes a project by name, with a path relative to the root of AndroidX.
- */
- def includeProject(String name, String filePath) {
- if (supportRootDir == null) {
- throw new GradleException("Must call setupPlayground() first.")
- }
- includeProjectAt(name, new File(supportRootDir, filePath))
- }
-
- /**
- * Initializes the playground project to use public repositories as well as other internal projects
- * that cannot be found in public repositories.
- *
- * @param settings The reference to the settings script
- * @param relativePathToRoot The relative path of the project to the root AndroidX project
- */
- def setupPlayground(String relativePathToRoot) {
- def projectDir = settings.rootProject.getProjectDir()
- def supportRoot = new File(projectDir, relativePathToRoot).getCanonicalFile()
- this.supportRootDir = supportRoot
- def buildFile = new File(supportRoot, "playground-common/playground-build.gradle")
- def relativePathToBuild = projectDir.toPath().relativize(buildFile.toPath()).toString()
-
- Properties playgroundProperties = new Properties()
- File propertiesFile = new File(supportRoot, "playground-common/playground.properties")
- propertiesFile.withInputStream {
- playgroundProperties.load(it)
- }
- settings.gradle.beforeProject { project ->
- // load playground properties. These are not kept in the playground projects to prevent
- // AndroidX build from reading them.
- playgroundProperties.each {
- project.ext[it.key] = it.value
- }
- }
-
- settings.rootProject.buildFileName = relativePathToBuild
- settings.enableFeaturePreview("VERSION_CATALOGS")
-
- def catalogFiles = objectFactory.fileCollection().from("$supportRoot/gradle/libs.versions.toml")
- settings.dependencyResolutionManagement {
- versionCatalogs {
- libs {
- from(catalogFiles)
- }
- }
- }
-
- includeProject(":lint-checks", "lint-checks")
- includeProject(":lint-checks:integration-tests", "lint-checks/integration-tests")
- includeProject(":fakeannotations", "fakeannotations")
- includeProject(":internal-testutils-common", "testutils/testutils-common")
- includeProject(":internal-testutils-gradle-plugin", "testutils/testutils-gradle-plugin")
-
- // allow public repositories
- System.setProperty("ALLOW_PUBLIC_REPOS", "true")
-
- // specify out dir location
- System.setProperty("CHECKOUT_ROOT", supportRoot.path)
- }
-
-
- /**
- * A convenience method to include projects from the main AndroidX build using a filter.
- *
- * @param filter This filter will be called with the project name (project path in gradle).
- * If filter returns true, it will be included in the build.
- */
- def selectProjectsFromAndroidX(filter) {
- if (supportRootDir == null) {
- throw new RuntimeException("Must call setupPlayground() first.")
- }
-
- // Multiline matcher for anything of the form:
- // includeProject(name, path, ...)
- // where '...' is anything except the ')' character.
- def includeProjectPattern = ~/(?m)^[\n\r\s]*includeProject\("(?<name>[a-z0-9-:]*)",[\n\r\s]*"(?<path>[a-z0-9-\/]+)[^)]+\)$/
- def supportSettingsFile = new File(supportRootDir, "settings.gradle")
- def matcher = includeProjectPattern.matcher(supportSettingsFile.text)
-
- while (matcher.find()) {
- // check if is an include project line, if so, extract project gradle path and
- // file system path and call the filter
- def projectGradlePath = matcher.group("name")
- def projectFilePath = matcher.group("path")
- if (filter(projectGradlePath)) {
- includeProject(projectGradlePath, projectFilePath)
- }
- }
- }
-
- /**
- * Checks if a project is necessary for playground projects that involve compose.
- */
- def isNeededForComposePlayground(name) {
- if (name == ":compose:lint:common") return true
- if (name == ":compose:lint:internal-lint-checks") return true
- if (name == ":compose:test-utils") return true
- if (name == ":compose:lint:common-test") return true
- if (name == ":test:screenshot:screenshot") return true
- return false
- }
-}
\ No newline at end of file
diff --git a/playground-common/playground-plugin/src/main/groovy/androidx/playground/PlaygroundPlugin.groovy b/playground-common/playground-plugin/src/main/groovy/androidx/playground/PlaygroundPlugin.groovy
deleted file mode 100644
index 4208b82..0000000
--- a/playground-common/playground-plugin/src/main/groovy/androidx/playground/PlaygroundPlugin.groovy
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package androidx.playground
-
-import org.gradle.api.Plugin
-import org.gradle.api.initialization.Settings
-
-class PlaygroundPlugin implements Plugin<Settings> {
- void apply(Settings settings) {
- settings.apply(plugin: "playground-ge-conventions")
-
- settings.extensions.create("playground", PlaygroundExtension, settings)
-
- validateJvm(settings)
- }
-
- def validateJvm(Settings settings) {
- // validate JVM version to print an understandable error if it is not set to the
- // required value (11)
- def jvmVersion = System.getProperty("java.vm.specification.version")
- if (jvmVersion != "11") {
- def guidance;
- if (settings.gradle.startParameter.projectProperties.containsKey("android.injected.invoked.from.ide")) {
- guidance = "Make sure to set the gradle JDK to JDK 11 in the project settings." +
- "(File -> Other Settings -> Default Project Structure)"
- } else {
- guidance = "Make sure your JAVA_HOME environment variable points to Java 11 JDK."
- }
- throw new IllegalStateException("""
- AndroidX build must be invoked with JDK 11.
- $guidance
- Current version: $jvmVersion
- Current JAVA HOME: ${System.getProperty("java.home")}""".stripIndent());
- }
-
- }
-}
diff --git a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleEnterpriseConventionsPlugin.kt b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleEnterpriseConventionsPlugin.kt
new file mode 100644
index 0000000..ef5a878
--- /dev/null
+++ b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/GradleEnterpriseConventionsPlugin.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.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.URI
+
+class GradleEnterpriseConventionsPlugin : Plugin<Settings> {
+ override fun apply(settings: Settings) {
+ settings.apply(mapOf("plugin" to "com.gradle.enterprise"))
+ 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"
+
+ buildScan.apply {
+ publishAlways()
+ (this as BuildScanExtensionWithHiddenFeatures).publishIfAuthenticated()
+ isUploadInBackground = !isCI
+ capture.isTaskInputFiles = true
+
+ GradleWorkaround.obfuscate(obfuscation)
+ }
+ }
+
+ settings.buildCache.remote(HttpBuildCache::class.java) { remote ->
+ remote.url = URI("https://ge.androidx.dev/cache/")
+ val buildCachePassword = System.getenv("GRADLE_BUILD_CACHE_PASSWORD")
+ if (isCI && !buildCachePassword.isNullOrEmpty()) {
+ remote.isPush = true
+ remote.credentials { credentials ->
+ credentials.username = "ci"
+ credentials.password = buildCachePassword
+ }
+ } else {
+ remote.isPush = false
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt
new file mode 100644
index 0000000..90a699a
--- /dev/null
+++ b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.playground
+
+import org.gradle.api.GradleException
+import org.gradle.api.initialization.Settings
+import org.gradle.api.model.ObjectFactory
+import java.io.File
+import java.util.Properties
+import javax.inject.Inject
+
+open class PlaygroundExtension @Inject constructor(
+ private val settings: Settings,
+ private val objectFactory: ObjectFactory
+) {
+ private var supportRootDir: File? = null
+
+ /**
+ * Includes the project if it does not already exist.
+ * This is invoked from `includeProject` to ensure all parent projects are included. If they are
+ * not, gradle will use the root project path to set the projectDir, which might conflict in
+ * playground. Instead, this method checks if another project in that path exists and if so,
+ * changes the project dir to avoid the conflict.
+ * see b/197253160 for details.
+ */
+ private fun includeFakeParentProjectIfNotExists(name: String, projectDir: File) {
+ if (name.isEmpty()) return
+ if (settings.findProject(name) != null) {
+ return
+ }
+ val actualProjectDir: File = if (settings.findProject(projectDir) != null) {
+ // Project directory conflicts with an existing project (possibly root). Move it
+ // to another directory to avoid the conflict.
+ File(projectDir.parentFile, ".ignore-${projectDir.name}")
+ } else {
+ projectDir
+ }
+ includeProjectAt(name, actualProjectDir)
+ // Set it to a gradle file that does not exist.
+ // We must always include projects starting with root, if we are including nested projects.
+ settings.project(name).buildFileName = "ignored.gradle"
+ }
+
+ private fun includeProjectAt(name: String, projectDir: File) {
+ if (settings.findProject(name) != null) {
+ throw GradleException("Cannot include project twice: $name is already included.")
+ }
+ val parentPath = name.substring(0, name.lastIndexOf(":"))
+ val parentDir = projectDir.parentFile
+ // Make sure parent is created first. see: b/197253160 for details
+ includeFakeParentProjectIfNotExists(
+ parentPath,
+ parentDir
+ )
+ settings.include(name)
+ settings.project(name).projectDir = projectDir
+ }
+
+ /**
+ * Includes a project by name, with a path relative to the root of AndroidX.
+ */
+ fun includeProject(name: String, filePath: String) {
+ if (supportRootDir == null) {
+ throw GradleException("Must call setupPlayground() first.")
+ }
+ includeProjectAt(name, File(supportRootDir, filePath))
+ }
+
+ /**
+ * Initializes the playground project to use public repositories as well as other internal
+ * projects that cannot be found in public repositories.
+ *
+ * @param relativePathToRoot The relative path of the project to the root AndroidX project
+ */
+ fun setupPlayground(relativePathToRoot: String) {
+ val projectDir = settings.rootProject.projectDir
+ val supportRoot = File(projectDir, relativePathToRoot).canonicalFile
+ this.supportRootDir = supportRoot
+ val buildFile = File(supportRoot, "playground-common/playground-build.gradle")
+ val relativePathToBuild = projectDir.toPath().relativize(buildFile.toPath()).toString()
+
+ val playgroundProperties = Properties()
+ val propertiesFile = File(supportRoot, "playground-common/playground.properties")
+ playgroundProperties.load(propertiesFile.inputStream())
+ settings.gradle.beforeProject { project ->
+ // load playground properties. These are not kept in the playground projects to prevent
+ // AndroidX build from reading them.
+ playgroundProperties.forEach {
+ project.extensions.extraProperties[it.key as String] = it.value
+ }
+ }
+
+ settings.rootProject.buildFileName = relativePathToBuild
+ settings.enableFeaturePreview("VERSION_CATALOGS")
+
+ val catalogFiles =
+ objectFactory.fileCollection().from("$supportRoot/gradle/libs.versions.toml")
+ settings.dependencyResolutionManagement {
+ it.versionCatalogs.create("libs").from(catalogFiles)
+ }
+
+ includeProject(":lint-checks", "lint-checks")
+ includeProject(":lint-checks:integration-tests", "lint-checks/integration-tests")
+ includeProject(":fakeannotations", "fakeannotations")
+ includeProject(":internal-testutils-common", "testutils/testutils-common")
+ includeProject(":internal-testutils-gradle-plugin", "testutils/testutils-gradle-plugin")
+
+ // allow public repositories
+ System.setProperty("ALLOW_PUBLIC_REPOS", "true")
+
+ // specify out dir location
+ System.setProperty("CHECKOUT_ROOT", supportRoot.path)
+ }
+
+ /**
+ * A convenience method to include projects from the main AndroidX build using a filter.
+ *
+ * @param filter This filter will be called with the project name (project path in gradle).
+ * If filter returns true, it will be included in the build.
+ */
+ fun selectProjectsFromAndroidX(filter: (String) -> Boolean) {
+ if (supportRootDir == null) {
+ throw RuntimeException("Must call setupPlayground() first.")
+ }
+
+ // Multiline matcher for anything of the form:
+ // includeProject(name, path, ...)
+ // where '...' is anything except the ')' character.
+ /* ktlint-disable max-line-length */
+ val includeProjectPattern = Regex(
+ """[\n\r\s]*includeProject\("(?<name>[a-z0-9-:]*)",[\n\r\s]*"(?<path>[a-z0-9-\/]+)[^)]+\)$""",
+ setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE)
+ ).toPattern()
+ val supportSettingsFile = File(supportRootDir, "settings.gradle")
+ val matcher = includeProjectPattern.matcher(supportSettingsFile.readText())
+
+ while (matcher.find()) {
+ // check if is an include project line, if so, extract project gradle path and
+ // file system path and call the filter
+ val projectGradlePath = matcher.group("name")
+ val projectFilePath = matcher.group("path")
+ if (filter(projectGradlePath)) {
+ includeProject(projectGradlePath, projectFilePath)
+ }
+ }
+ }
+
+ /**
+ * Checks if a project is necessary for playground projects that involve compose.
+ */
+ fun isNeededForComposePlayground(name: String): Boolean {
+ if (name == ":compose:lint:common") return true
+ if (name == ":compose:lint:internal-lint-checks") return true
+ if (name == ":compose:test-utils") return true
+ if (name == ":compose:lint:common-test") return true
+ if (name == ":test:screenshot:screenshot") return true
+ return false
+ }
+}
\ 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
new file mode 100644
index 0000000..ff190ae
--- /dev/null
+++ b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundPlugin.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.playground
+
+import org.gradle.api.Plugin
+import org.gradle.api.initialization.Settings
+
+class PlaygroundPlugin : Plugin<Settings> {
+ override fun apply(settings: Settings) {
+ settings.apply(mapOf("plugin" to "playground-ge-conventions"))
+ settings.extensions.create("playground", PlaygroundExtension::class.java, settings)
+ validateJvm(settings)
+ }
+
+ private fun validateJvm(settings: Settings) {
+ // validate JVM version to print an understandable error if it is not set to the
+ // required value (11)
+ val jvmVersion = System.getProperty("java.vm.specification.version")
+ check(jvmVersion == "11") {
+ """
+ AndroidX build must be invoked with JDK 11.
+ ${
+ if (settings.gradle.startParameter.projectProperties.containsKey(
+ "android.injected.invoked.from.ide"
+ )
+ ) {
+ """
+ Make sure to set the Gradle JDK to JDK 11 in the project settings.
+ File -> Settings -> Build, Execution, Deployment -> Build Tools ->
+ Gradle -> Gradle JDK"
+ """
+ } else {
+ "Make sure your JAVA_HOME environment variable points to Java 11 JDK."
+ }
+ }
+ Current version: $jvmVersion
+ Current JAVA HOME: ${System.getProperty("java.home")}
+ """.trimIndent()
+ }
+ }
+}
\ No newline at end of file
diff --git a/preference/OWNERS b/preference/OWNERS
index 50b530e..cc4dc56 100644
--- a/preference/OWNERS
+++ b/preference/OWNERS
@@ -1,2 +1 @@
-pavlis@google.com
lpf@google.com
diff --git a/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt b/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt
index 83d0a926..4ece2c3 100644
--- a/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt
+++ b/profileinstaller/integration-tests/init-macrobenchmark/src/androidTest/java/androidx/profileinstaller/integration/macrobenchmark/ProfileinstallerStartupBenchmark.kt
@@ -20,6 +20,7 @@
import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.filters.LargeTest
+import androidx.testutils.BASIC_COMPILATION_MODES
import androidx.testutils.createStartupCompilationParams
import androidx.testutils.measureStartup
import org.junit.Rule
@@ -48,6 +49,8 @@
companion object {
@Parameterized.Parameters(name = "startup={0},compilation={1}")
@JvmStatic
- fun parameters() = createStartupCompilationParams()
+ fun parameters() = createStartupCompilationParams(
+ compilationModes = BASIC_COMPILATION_MODES
+ )
}
}
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt
index 60e92ca..a644cdd 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt
@@ -20,7 +20,7 @@
import androidx.room.compiler.processing.ksp.KSFileAsOriginatingElement
import androidx.room.compiler.processing.ksp.KspElement
import androidx.room.compiler.processing.ksp.KspMemberContainer
-import androidx.room.compiler.processing.ksp.containingFileAsOriginatingElement
+import androidx.room.compiler.processing.ksp.wrapAsOriginatingElement
import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
import javax.lang.model.element.Element
import kotlin.contracts.contract
@@ -129,13 +129,13 @@
return when (this) {
is JavacElement -> element
is KspElement -> {
- declaration.containingFileAsOriginatingElement()
+ declaration.wrapAsOriginatingElement()
}
is KspSyntheticPropertyMethodElement -> {
- field.declaration.containingFileAsOriginatingElement()
+ field.declaration.wrapAsOriginatingElement()
}
is KspMemberContainer -> {
- declaration?.containingFileAsOriginatingElement()
+ declaration?.wrapAsOriginatingElement()
}
else -> error("Originating element is not implemented for ${this.javaClass}")
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt
index f10956c..20f735b 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSAnnotatedExt.kt
@@ -17,6 +17,7 @@
package androidx.room.compiler.processing.ksp
import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSDeclaration
private fun KSAnnotated.hasAnnotationWithQName(qName: String) = annotations.any {
@@ -33,11 +34,15 @@
internal fun KSAnnotated.hasJvmDefaultAnnotation() = hasAnnotationWithQName("kotlin.jvm.JvmDefault")
/**
- * Return a reference to the containing file that implements the
+ * Return a reference to the containing file or class declaration via a wrapper that implements the
* [javax.lang.model.element.Element] API so that we can report it to JavaPoet.
*/
-internal fun KSAnnotated.containingFileAsOriginatingElement(): KSFileAsOriginatingElement? {
- return (this as? KSDeclaration)?.containingFile?.let {
+internal fun KSAnnotated.wrapAsOriginatingElement(): OriginatingElementWrapper? {
+ val ksDeclaration = this as? KSDeclaration ?: return null
+
+ return ksDeclaration.containingFile?.let {
KSFileAsOriginatingElement(it)
+ } ?: (ksDeclaration as? KSClassDeclaration)?.let {
+ KSClassDeclarationAsOriginatingElement(it)
}
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt
index e2a0758..373d9607 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFiler.kt
@@ -21,6 +21,7 @@
import androidx.room.compiler.processing.util.ISSUE_TRACKER_LINK
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFile
import com.squareup.javapoet.JavaFile
import com.squareup.kotlinpoet.FileSpec
@@ -34,11 +35,11 @@
private val messager: XMessager,
) : XFiler {
override fun write(javaFile: JavaFile, mode: XFiler.Mode) {
- val originatingFiles = javaFile.typeSpec.originatingElements
- .map(::originatingFileFor)
+ val originatingElements = javaFile.typeSpec.originatingElements
+ .toOriginatingElements()
createNewFile(
- originatingFiles = originatingFiles,
+ originatingElements = originatingElements,
packageName = javaFile.packageName,
fileName = javaFile.typeSpec.name,
extensionName = "java",
@@ -51,13 +52,13 @@
}
override fun write(fileSpec: FileSpec, mode: XFiler.Mode) {
- val originatingFiles = fileSpec.members
+ val originatingElements = fileSpec.members
.filterIsInstance<OriginatingElementsHolder>()
.flatMap { it.originatingElements }
- .map(::originatingFileFor)
+ .toOriginatingElements()
createNewFile(
- originatingFiles = originatingFiles,
+ originatingElements = originatingElements,
packageName = fileSpec.packageName,
fileName = fileSpec.name,
extensionName = "kt",
@@ -69,21 +70,14 @@
}
}
- private fun originatingFileFor(element: Element): KSFile {
- check(element is KSFileAsOriginatingElement) {
- "Unexpected element type in originating elements. $element"
- }
- return element.ksFile
- }
-
private fun createNewFile(
- originatingFiles: List<KSFile>,
+ originatingElements: OriginatingElements,
packageName: String,
fileName: String,
extensionName: String,
aggregating: Boolean
): OutputStream {
- val dependencies = if (originatingFiles.isEmpty()) {
+ val dependencies = if (originatingElements.isEmpty()) {
messager.printMessage(
Diagnostic.Kind.WARNING,
"""
@@ -96,7 +90,16 @@
} else {
Dependencies(
aggregating = aggregating,
- sources = originatingFiles.distinct().toTypedArray()
+ sources = originatingElements.files.distinct().toTypedArray()
+ )
+ }
+
+ if (originatingElements.classes.isNotEmpty()) {
+ delegate.associateWithClasses(
+ classes = originatingElements.classes,
+ packageName = packageName,
+ fileName = fileName,
+ extensionName = extensionName
)
}
@@ -107,4 +110,26 @@
extensionName = extensionName
)
}
+
+ private data class OriginatingElements(
+ val files: List<KSFile>,
+ val classes: List<KSClassDeclaration>,
+ ) {
+ fun isEmpty(): Boolean = files.isEmpty() && classes.isEmpty()
+ }
+
+ private fun List<Element>.toOriginatingElements(): OriginatingElements {
+ val files = mutableListOf<KSFile>()
+ val classes = mutableListOf<KSClassDeclaration>()
+
+ forEach { element ->
+ when (element) {
+ is KSFileAsOriginatingElement -> files.add(element.ksFile)
+ is KSClassDeclarationAsOriginatingElement -> classes.add(element.ksClassDeclaration)
+ else -> error("Unexpected element type in originating elements. $element")
+ }
+ }
+
+ return OriginatingElements(files, classes)
+ }
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFileAsOriginatingElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/OriginatingElementWrappers.kt
similarity index 72%
rename from room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFileAsOriginatingElement.kt
rename to room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/OriginatingElementWrappers.kt
index ec05ba1..30aaa85 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSFileAsOriginatingElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/OriginatingElementWrappers.kt
@@ -16,6 +16,7 @@
package androidx.room.compiler.processing.ksp
+import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFile
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.Element
@@ -26,12 +27,27 @@
import javax.lang.model.type.TypeMirror
/**
- * When generating java code, JavaPoet only provides an API that receives Element.
- * This wrapper class helps us wrap a KSFile as an originating element and KspFiler unwraps it to
- * get the actual KSFile out of it.
+ * Implementation of [OriginatingElementWrapper] to wrap a [KSFile] for the case of a dependency
+ * within the sources being processed.
*/
internal data class KSFileAsOriginatingElement(
val ksFile: KSFile
+) : OriginatingElementWrapper(ksFile.fileName)
+
+/**
+ * Implementation of [OriginatingElementWrapper] to wrap a [KSClassDeclaration] for the case of
+ * a dependency in the classpath, in which case a [KSFile] is not available.
+ */
+internal data class KSClassDeclarationAsOriginatingElement(
+ val ksClassDeclaration: KSClassDeclaration
+) : OriginatingElementWrapper(ksClassDeclaration.simpleName.asString())
+
+/**
+ * When generating java code, JavaPoet only provides an API that receives Element.
+ * This wrapper class helps us wrap KSP constructs which KspFiler can unwrap later.
+ */
+internal sealed class OriginatingElementWrapper(
+ val elementSimpleName: String
) : Element {
override fun getAnnotationMirrors(): List<AnnotationMirror> {
return emptyList()
@@ -48,7 +64,7 @@
override fun asType(): TypeMirror {
throw UnsupportedOperationException(
- "KSFileAsOriginatingElement cannot be converted to a type"
+ "${this::class.simpleName} cannot be converted to a type"
)
}
@@ -61,7 +77,7 @@
}
override fun getSimpleName(): Name {
- return NameImpl(ksFile.fileName)
+ return NameImpl(elementSimpleName)
}
override fun getEnclosingElement(): Element? {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt
index e32fb9a..1c18366 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/OriginatingElementsTest.kt
@@ -16,6 +16,7 @@
package androidx.room.compiler.processing
+import androidx.room.compiler.processing.ksp.KSClassDeclarationAsOriginatingElement
import androidx.room.compiler.processing.ksp.KSFileAsOriginatingElement
import androidx.room.compiler.processing.ksp.KspTypeElement
import androidx.room.compiler.processing.ksp.synthetic.KspSyntheticPropertyMethodElement
@@ -72,6 +73,34 @@
}
@Test
+ fun classPathTypeIsConvertedToOriginatingElement() {
+ runProcessorTest {
+ val element = it.processingEnv
+ .requireTypeElement("com.google.devtools.ksp.processing.SymbolProcessor")
+
+ val originatingElement = element.originatingElementForPoet()
+ assertThat(originatingElement).isNotNull()
+
+ if (it.isKsp) {
+ assertThat(originatingElement)
+ .isInstanceOf(KSClassDeclarationAsOriginatingElement::class.java)
+
+ val ksClassDeclaration =
+ (originatingElement as KSClassDeclarationAsOriginatingElement)
+ .ksClassDeclaration
+ assertThat(ksClassDeclaration)
+ .isEqualTo(
+ (element as KspTypeElement).declaration
+ )
+ } else {
+ assertThat(originatingElement).isInstanceOf(TypeElement::class.java)
+ assertThat((originatingElement as TypeElement).qualifiedName.toString())
+ .isEqualTo("com.google.devtools.ksp.processing.SymbolProcessor")
+ }
+ }
+ }
+
+ @Test
fun syntheticPropertyElementConvertedToOriginatingElement() {
runProcessorTest(
sources = listOf(
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
index 252acfc..e3cbe2f 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
@@ -93,6 +93,56 @@
}
}
+ @Test
+ fun originatingClassAddedForClassPathAndFileType() {
+ runKspTest(sources = listOf(simpleKotlinClass)) { invocation ->
+ val sourceElement = invocation.processingEnv.requireTypeElement("foo.bar.Baz")
+ val classPathElement = invocation.processingEnv
+ .requireTypeElement("com.google.devtools.ksp.processing.SymbolProcessor")
+
+ val fileWithType = FileSpec.builder("foo", "Bar.kt").apply {
+ addType(
+ TypeSpec.classBuilder("Bar").apply {
+ addOriginatingElement(sourceElement)
+ addOriginatingElement(classPathElement)
+ }.build()
+ )
+ }.build()
+
+ val codeGenerator = DependencyTrackingCodeGenerator()
+ KspFiler(codeGenerator, TestMessager()).write(fileWithType)
+ codeGenerator.fileDependencies[fileWithType.name]
+ .containsExactlySimpleKotlinClass()
+ val (file, classDeclarations) = codeGenerator.classDependencies.entries.single()
+ assertThat(file).isEqualTo("Bar.kt")
+ assertThat(classDeclarations.single())
+ .isEqualTo((classPathElement as KspTypeElement).declaration)
+ }
+ }
+
+ @Test
+ fun originatingClassAddedForClassPathType() {
+ runKspTest(sources = listOf()) { invocation ->
+ val classPathElement = invocation.processingEnv
+ .requireTypeElement("com.google.devtools.ksp.processing.SymbolProcessor")
+
+ val fileWithType = FileSpec.builder("foo", "Bar.kt").apply {
+ addType(
+ TypeSpec.classBuilder("Bar").apply {
+ addOriginatingElement(classPathElement)
+ }.build()
+ )
+ }.build()
+
+ val codeGenerator = DependencyTrackingCodeGenerator()
+ KspFiler(codeGenerator, TestMessager()).write(fileWithType)
+ val (file, classDeclarations) = codeGenerator.classDependencies.entries.single()
+ assertThat(file).isEqualTo("Bar.kt")
+ assertThat(classDeclarations.single())
+ .isEqualTo((classPathElement as KspTypeElement).declaration)
+ }
+ }
+
private fun Dependencies?.containsExactlySimpleKotlinClass() {
assertThat(this).isNotNull()
val originatingFiles = this!!.originatingFiles.map { it.fileName }
@@ -122,6 +172,7 @@
class DependencyTrackingCodeGenerator : CodeGenerator {
val fileDependencies = mutableMapOf<String, Dependencies>()
+ val classDependencies = mutableMapOf<String, MutableSet<KSClassDeclaration>>()
override val generatedFile: Collection<File>
get() = emptyList()
@@ -141,7 +192,7 @@
fileName: String,
extensionName: String
) {
- // no-op required override.
+ classDependencies.getOrPut(fileName) { mutableSetOf() }.addAll(classes)
}
override fun createNewFile(
diff --git a/room/room-paging/build.gradle b/room/room-paging/build.gradle
index 42d90c5..b25cd0d 100644
--- a/room/room-paging/build.gradle
+++ b/room/room-paging/build.gradle
@@ -47,7 +47,7 @@
implementation(project(":room:room-runtime"))
implementation(project(":room:room-ktx"))
- api("androidx.paging:paging-common:3.1.0-beta01")
+ api("androidx.paging:paging-common:3.1.0")
androidTestImplementation(libs.kotlinCoroutinesTest)
androidTestImplementation(libs.multidex)
diff --git a/room/room-runtime/build.gradle b/room/room-runtime/build.gradle
index 44afc76..48cdb86 100644
--- a/room/room-runtime/build.gradle
+++ b/room/room-runtime/build.gradle
@@ -34,8 +34,8 @@
dependencies {
api(project(":room:room-common"))
- api("androidx.sqlite:sqlite-framework:2.2.0-beta01")
- api("androidx.sqlite:sqlite:2.2.0-beta01")
+ api(project(":sqlite:sqlite-framework"))
+ api(project(":sqlite:sqlite"))
implementation("androidx.arch.core:core-runtime:2.0.1")
compileOnly("androidx.paging:paging-common:2.0.0")
compileOnly("androidx.lifecycle:lifecycle-livedata-core:2.0.0")
diff --git a/room/room-testing/build.gradle b/room/room-testing/build.gradle
index d665ee7..1c0acde 100644
--- a/room/room-testing/build.gradle
+++ b/room/room-testing/build.gradle
@@ -25,8 +25,8 @@
dependencies {
api(project(":room:room-common"))
api(project(":room:room-runtime"))
- api("androidx.sqlite:sqlite:2.2.0-beta01")
- api("androidx.sqlite:sqlite-framework:2.2.0-beta01")
+ api(project(":sqlite:sqlite"))
+ api(project(":sqlite:sqlite-framework"))
api(project(":room:room-migration"))
implementation("androidx.arch.core:core-runtime:2.0.1")
api(libs.junit)
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/accessibility/AccessibilityDelegateSupportActivity.java b/samples/Support4Demos/src/main/java/com/example/android/supportv4/accessibility/AccessibilityDelegateSupportActivity.java
index bc37aa5..25a6bbc 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/accessibility/AccessibilityDelegateSupportActivity.java
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/accessibility/AccessibilityDelegateSupportActivity.java
@@ -23,6 +23,7 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
+import androidx.annotation.NonNull;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -73,7 +74,8 @@
// introduced the delegate off the developer.
ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegateCompat() {
@Override
- public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ public void onPopulateAccessibilityEvent(
+ @NonNull View host, @NonNull AccessibilityEvent event) {
super.onPopulateAccessibilityEvent(host, event);
// Note that View.onPopulateAccessibilityEvent was introduced in
// ICS and we would like to tweak a bit the text that is reported to
@@ -83,8 +85,8 @@
}
@Override
- public void onInitializeAccessibilityNodeInfo(View host,
- AccessibilityNodeInfoCompat info) {
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+ @NonNull AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
// Note that View.onInitializeAccessibilityNodeInfo was introduced in
// ICS and we would like to tweak a bit the text that is reported to
diff --git a/samples/Support4Demos/src/main/java/com/example/android/supportv4/accessibility/AccessibilityRoleDescriptionSupportActivity.java b/samples/Support4Demos/src/main/java/com/example/android/supportv4/accessibility/AccessibilityRoleDescriptionSupportActivity.java
index 963690a..2dbb078 100644
--- a/samples/Support4Demos/src/main/java/com/example/android/supportv4/accessibility/AccessibilityRoleDescriptionSupportActivity.java
+++ b/samples/Support4Demos/src/main/java/com/example/android/supportv4/accessibility/AccessibilityRoleDescriptionSupportActivity.java
@@ -22,6 +22,7 @@
import android.widget.Button;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -86,8 +87,8 @@
}
@Override
- public void onInitializeAccessibilityNodeInfo(View host,
- AccessibilityNodeInfoCompat info) {
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+ @NonNull AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
// This call will succeed on all platforms, but it will only set the role description
// on devices running KitKat (API 19) or later. On older platforms the method call
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/SampleMediaRouterActivity.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/SampleMediaRouterActivity.java
index dc5253e..6f72b20 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/SampleMediaRouterActivity.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/SampleMediaRouterActivity.java
@@ -756,7 +756,7 @@
@Override
public MediaRouterParams getRouterParams() {
return new MediaRouterParams.Builder(super.getRouterParams())
- .setMediaTransferReceiverDisabled(true)
+ .setMediaTransferReceiverEnabled(false)
.build();
}
}
diff --git a/samples/SupportPreferenceDemos/OWNERS b/samples/SupportPreferenceDemos/OWNERS
index f1b8c7e..b2c38b4 100644
--- a/samples/SupportPreferenceDemos/OWNERS
+++ b/samples/SupportPreferenceDemos/OWNERS
@@ -1,2 +1 @@
-pavlis@google.com
lpf@google.com
\ No newline at end of file
diff --git a/savedstate/OWNERS b/savedstate/OWNERS
index 8f61468..cf853fe 100644
--- a/savedstate/OWNERS
+++ b/savedstate/OWNERS
@@ -1,3 +1,5 @@
ilake@google.com
+jbwoods@google.com
+sanura@google.com
sergeyv@google.com
yboyar@google.com
diff --git a/savedstate/savedstate/src/androidTest/java/androidx/savedstate/SavedStateRegistryTest.kt b/savedstate/savedstate/src/androidTest/java/androidx/savedstate/SavedStateRegistryTest.kt
index 702bb98..0bac387 100644
--- a/savedstate/savedstate/src/androidTest/java/androidx/savedstate/SavedStateRegistryTest.kt
+++ b/savedstate/savedstate/src/androidTest/java/androidx/savedstate/SavedStateRegistryTest.kt
@@ -24,6 +24,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
@@ -172,6 +173,17 @@
@UiThreadTest
@Test
+ fun emptyBundle() {
+ val owner = FakeSavedStateRegistryOwner()
+ val outBundle = Bundle()
+ owner.savedStateRegistryController.performSave(outBundle)
+ assertWithMessage("Bundle $outBundle should be empty")
+ .that(outBundle.isEmpty)
+ .isTrue()
+ }
+
+ @UiThreadTest
+ @Test
fun runOnNextRecreationFromEarlyRegisteredObserver() {
val owner = FakeSavedStateRegistryOwner()
owner.savedStateRegistryController.performAttach()
diff --git a/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistry.java b/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistry.java
index a8a4ef2..adeae54 100644
--- a/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistry.java
+++ b/savedstate/savedstate/src/main/java/androidx/savedstate/SavedStateRegistry.java
@@ -239,7 +239,9 @@
Map.Entry<String, SavedStateProvider> entry1 = it.next();
components.putBundle(entry1.getKey(), entry1.getValue().saveState());
}
- outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
+ if (!components.isEmpty()) {
+ outBundle.putBundle(SAVED_COMPONENTS_KEY, components);
+ }
}
/**
diff --git a/settings.gradle b/settings.gradle
index 40046e4..7d8f07b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -427,6 +427,7 @@
includeProject(":core:core-google-shortcuts", "core/core-google-shortcuts", [BuildType.MAIN])
includeProject(":core:core-ktx", "core/core-ktx", [BuildType.MAIN])
includeProject(":core:core-performance", "core/core-performance", [BuildType.MAIN])
+includeProject(":core:core-performance:core-performance-samples", "core/core-performance/samples", [BuildType.MAIN])
includeProject(":core:core-remoteviews", "core/core-remoteviews", [BuildType.MAIN])
includeProject(":core:core-remoteviews:integration-tests:demos", "core/core-remoteviews/integration-tests/demos", [BuildType.MAIN])
includeProject(":core:core-splashscreen", "core/core-splashscreen", [BuildType.MAIN])
diff --git a/slice/slice-view/src/main/res/values-it/strings.xml b/slice/slice-view/src/main/res/values-it/strings.xml
index 9087d7a..62c8052 100644
--- a/slice/slice-view/src/main/res/values-it/strings.xml
+++ b/slice/slice-view/src/main/res/values-it/strings.xml
@@ -22,16 +22,16 @@
<string name="abc_slice_show_more" msgid="1112789899890391107">"Mostra altro"</string>
<string name="abc_slice_updated" msgid="7932359091871934205">"Ultimo aggiornamento: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<plurals name="abc_slice_duration_min" formatted="false" msgid="7664017844210142826">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> min fa</item>
<item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min fa</item>
- <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min fa</item>
</plurals>
<plurals name="abc_slice_duration_years" formatted="false" msgid="2628491538787454021">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> anno fa</item>
<item quantity="other"><xliff:g id="ID_2">%d</xliff:g> anni fa</item>
- <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> anno fa</item>
</plurals>
<plurals name="abc_slice_duration_days" formatted="false" msgid="8356547162075064530">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> giorno fa</item>
<item quantity="other"><xliff:g id="ID_2">%d</xliff:g> giorni fa</item>
- <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> giorno fa</item>
</plurals>
<string name="abc_slice_error" msgid="1794214973158263497">"Impossibile collegarsi"</string>
</resources>
diff --git a/slice/slice-view/src/main/res/values-pt-rPT/strings.xml b/slice/slice-view/src/main/res/values-pt-rPT/strings.xml
index 34a67ff..08c5c19 100644
--- a/slice/slice-view/src/main/res/values-pt-rPT/strings.xml
+++ b/slice/slice-view/src/main/res/values-pt-rPT/strings.xml
@@ -22,16 +22,16 @@
<string name="abc_slice_show_more" msgid="1112789899890391107">"Mostrar mais"</string>
<string name="abc_slice_updated" msgid="7932359091871934205">"Atualização: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<plurals name="abc_slice_duration_min" formatted="false" msgid="7664017844210142826">
- <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> min</item>
<item quantity="one">Há <xliff:g id="ID_1">%d</xliff:g> min</item>
+ <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> min</item>
</plurals>
<plurals name="abc_slice_duration_years" formatted="false" msgid="2628491538787454021">
- <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> anos</item>
<item quantity="one">Há <xliff:g id="ID_1">%d</xliff:g> ano</item>
+ <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> anos</item>
</plurals>
<plurals name="abc_slice_duration_days" formatted="false" msgid="8356547162075064530">
- <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> dias</item>
<item quantity="one">Há <xliff:g id="ID_1">%d</xliff:g> dia</item>
+ <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> dias</item>
</plurals>
<string name="abc_slice_error" msgid="1794214973158263497">"Não foi possível ligar"</string>
</resources>
diff --git a/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt b/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt
index ad1ce6b..42a98af 100644
--- a/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt
+++ b/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt
@@ -27,14 +27,33 @@
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
/**
- * Default compilation modes to test for all AndroidX macrobenchmarks.
+ * Basic, always-usable compilation modes, when baseline profiles aren't available.
+ *
+ * Over time, it's expected very few macrobenchmarks will reference this directly, as more libraries
+ * gain baseline profiles.
*/
-val COMPILATION_MODES = listOf(
- CompilationMode.None,
- CompilationMode.Interpreted,
- CompilationMode.BaselineProfile,
- CompilationMode.SpeedProfile()
-)
+val BASIC_COMPILATION_MODES = if (Build.VERSION.SDK_INT < 24) {
+ // other modes aren't supported
+ listOf(CompilationMode.None)
+} else {
+ listOf(
+ CompilationMode.None,
+ CompilationMode.Interpreted,
+ CompilationMode.SpeedProfile()
+ )
+}
+
+/**
+ * Default compilation modes to test for all AndroidX macrobenchmarks.
+ *
+ * Baseline profiles are only supported from Nougat (API 24),
+ * currently through Android 11 (API 30)
+ */
+val COMPILATION_MODES = if (Build.VERSION.SDK_INT in 24..30) {
+ listOf(CompilationMode.BaselineProfile)
+} else {
+ emptyList()
+} + BASIC_COMPILATION_MODES
/**
* Temporary, while transitioning to new metrics
@@ -73,7 +92,11 @@
StartupMode.HOT,
StartupMode.WARM,
StartupMode.COLD
- ),
+ ).filter {
+ // skip StartupMode.HOT on Angler, API 23 - it works locally with same build on Bullhead,
+ // but not in Jetpack CI (b/204572406)
+ !(Build.VERSION.SDK_INT == 23 && it == StartupMode.HOT && Build.DEVICE == "angler")
+ },
compilationModes: List<CompilationMode> = COMPILATION_MODES
): List<Array<Any>> = mutableListOf<Array<Any>>().apply {
for (startupMode in startupModes) {
diff --git a/text/text/src/main/java/androidx/compose/ui/text/android/TempListUtils.kt b/text/text/src/main/java/androidx/compose/ui/text/android/TempListUtils.kt
index 2de2410..982d05c 100644
--- a/text/text/src/main/java/androidx/compose/ui/text/android/TempListUtils.kt
+++ b/text/text/src/main/java/androidx/compose/ui/text/android/TempListUtils.kt
@@ -24,6 +24,10 @@
/**
* Iterates through a [List] using the index and calls [action] for each item.
* This does not allocate an iterator like [Iterable.forEach].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastForEach(action: (T) -> Unit) {
@@ -37,6 +41,10 @@
/**
* Applies the given [transform] function to each element of the original collection
* and appends the results to the given [destination].
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R, C : MutableCollection<in R>> List<T>.fastMapTo(
@@ -55,6 +63,10 @@
* to each pair of two adjacent elements in this collection.
*
* The returned list is empty if this collection contains less than two elements.
+ *
+ * **Do not use for collections that come from public APIs**, since they may not support random
+ * access in an efficient way, and this method may actually be a lot slower. Only use for
+ * collections that are created by code we control and are known to support random access.
*/
@OptIn(ExperimentalContracts::class)
internal inline fun <T, R> List<T>.fastZipWithNext(transform: (T, T) -> R): List<R> {
diff --git a/wear/benchmark/integration-tests/macrobenchmark-target/build.gradle b/wear/benchmark/integration-tests/macrobenchmark-target/build.gradle
index 78ff9ef..8b165bb 100644
--- a/wear/benchmark/integration-tests/macrobenchmark-target/build.gradle
+++ b/wear/benchmark/integration-tests/macrobenchmark-target/build.gradle
@@ -40,5 +40,6 @@
implementation projectOrArtifact(":activity:activity-ktx")
implementation 'androidx.core:core-ktx'
implementation(libs.material)
+ implementation(project(":profileinstaller:profileinstaller"))
implementation 'androidx.wear:wear:1.1.0'
}
diff --git a/wear/compose/compose-material/api/current.txt b/wear/compose/compose-material/api/current.txt
index a63d79c..8eab278 100644
--- a/wear/compose/compose-material/api/current.txt
+++ b/wear/compose/compose-material/api/current.txt
@@ -145,15 +145,15 @@
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues getButtonsContentPadding();
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues getChipsContentPadding();
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues getConfirmationContentPadding();
- method public long getIndefiniteDuration();
- method public long getLongDuration();
- method public long getShortDuration();
+ method public long getIndefiniteDurationMillis();
+ method public long getLongDurationMillis();
+ method public long getShortDurationMillis();
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.PaddingValues ButtonsContentPadding;
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.PaddingValues ChipsContentPadding;
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.PaddingValues ConfirmationContentPadding;
- property public final long IndefiniteDuration;
- property public final long LongDuration;
- property public final long ShortDuration;
+ property public final long IndefiniteDurationMillis;
+ property public final long LongDurationMillis;
+ property public final long ShortDurationMillis;
field public static final androidx.wear.compose.material.DialogDefaults INSTANCE;
}
@@ -178,7 +178,9 @@
public final class InlineSliderDefaults {
method @androidx.compose.runtime.Composable public androidx.wear.compose.material.InlineSliderColors colors(optional long backgroundColor, optional long spacerColor, optional long selectedBarColor, optional long unselectedBarColor, optional long disabledBackgroundColor, optional long disabledSpacerColor, optional long disabledSelectedBarColor, optional long disabledUnselectedBarColor);
method public androidx.compose.ui.graphics.vector.ImageVector getDecreaseIcon();
+ method public androidx.compose.ui.graphics.vector.ImageVector getIncreaseIcon();
property public final androidx.compose.ui.graphics.vector.ImageVector DecreaseIcon;
+ property public final androidx.compose.ui.graphics.vector.ImageVector IncreaseIcon;
field public static final androidx.wear.compose.material.InlineSliderDefaults INSTANCE;
}
@@ -233,7 +235,7 @@
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.wear.compose.material.ScalingLazyListState scalingLazyListState, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection);
- method @androidx.compose.runtime.Composable public static void PositionIndicator(float rsbRatio, optional androidx.compose.ui.Modifier modifier, optional long color, optional boolean reverseDirection);
+ method @androidx.compose.runtime.Composable public static void PositionIndicator(kotlin.jvm.functions.Function0<java.lang.Float> value, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> range, optional long color, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.wear.compose.material.PositionIndicatorState state, float indicatorHeight, float indicatorWidth, float paddingRight, optional androidx.compose.ui.Modifier modifier, optional long color, optional long background, optional boolean autoHide, optional boolean reverseDirection);
}
@@ -243,6 +245,9 @@
property public abstract float positionFraction;
}
+ public final class RangeDefaultsKt {
+ }
+
public final class Resources_androidKt {
}
@@ -366,6 +371,18 @@
method @androidx.compose.runtime.Composable public static void InlineSlider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, int steps, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional boolean segmented, optional kotlin.jvm.functions.Function0<kotlin.Unit> decreaseIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit> increaseIcon, optional androidx.wear.compose.material.InlineSliderColors colors);
}
+ public final class StepperDefaults {
+ method public androidx.compose.ui.graphics.vector.ImageVector getDecreaseIcon();
+ method public androidx.compose.ui.graphics.vector.ImageVector getIncreaseIcon();
+ property public final androidx.compose.ui.graphics.vector.ImageVector DecreaseIcon;
+ property public final androidx.compose.ui.graphics.vector.ImageVector IncreaseIcon;
+ field public static final androidx.wear.compose.material.StepperDefaults INSTANCE;
+ }
+
+ public final class StepperKt {
+ method @androidx.compose.runtime.Composable public static void Stepper(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, int steps, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit> decreaseIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit> increaseIcon, optional long backgroundColor, optional long contentColor, optional long iconTintColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ }
+
public final class SwipeToDismissBoxKt {
}
diff --git a/wear/compose/compose-material/api/public_plus_experimental_current.txt b/wear/compose/compose-material/api/public_plus_experimental_current.txt
index 8855f3c..789634c 100644
--- a/wear/compose/compose-material/api/public_plus_experimental_current.txt
+++ b/wear/compose/compose-material/api/public_plus_experimental_current.txt
@@ -145,15 +145,15 @@
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues getButtonsContentPadding();
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues getChipsContentPadding();
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues getConfirmationContentPadding();
- method public long getIndefiniteDuration();
- method public long getLongDuration();
- method public long getShortDuration();
+ method public long getIndefiniteDurationMillis();
+ method public long getLongDurationMillis();
+ method public long getShortDurationMillis();
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.PaddingValues ButtonsContentPadding;
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.PaddingValues ChipsContentPadding;
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.PaddingValues ConfirmationContentPadding;
- property public final long IndefiniteDuration;
- property public final long LongDuration;
- property public final long ShortDuration;
+ property public final long IndefiniteDurationMillis;
+ property public final long LongDurationMillis;
+ property public final long ShortDurationMillis;
field public static final androidx.wear.compose.material.DialogDefaults INSTANCE;
}
@@ -193,7 +193,9 @@
public final class InlineSliderDefaults {
method @androidx.compose.runtime.Composable public androidx.wear.compose.material.InlineSliderColors colors(optional long backgroundColor, optional long spacerColor, optional long selectedBarColor, optional long unselectedBarColor, optional long disabledBackgroundColor, optional long disabledSpacerColor, optional long disabledSelectedBarColor, optional long disabledUnselectedBarColor);
method public androidx.compose.ui.graphics.vector.ImageVector getDecreaseIcon();
+ method public androidx.compose.ui.graphics.vector.ImageVector getIncreaseIcon();
property public final androidx.compose.ui.graphics.vector.ImageVector DecreaseIcon;
+ property public final androidx.compose.ui.graphics.vector.ImageVector IncreaseIcon;
field public static final androidx.wear.compose.material.InlineSliderDefaults INSTANCE;
}
@@ -248,7 +250,7 @@
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.wear.compose.material.ScalingLazyListState scalingLazyListState, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection);
- method @androidx.compose.runtime.Composable public static void PositionIndicator(float rsbRatio, optional androidx.compose.ui.Modifier modifier, optional long color, optional boolean reverseDirection);
+ method @androidx.compose.runtime.Composable public static void PositionIndicator(kotlin.jvm.functions.Function0<java.lang.Float> value, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> range, optional long color, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.wear.compose.material.PositionIndicatorState state, float indicatorHeight, float indicatorWidth, float paddingRight, optional androidx.compose.ui.Modifier modifier, optional long color, optional long background, optional boolean autoHide, optional boolean reverseDirection);
}
@@ -258,6 +260,9 @@
property public abstract float positionFraction;
}
+ public final class RangeDefaultsKt {
+ }
+
@androidx.compose.runtime.Immutable @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class ResistanceConfig {
ctor public ResistanceConfig(float basis, optional float factorAtMin, optional float factorAtMax);
method public float computeResistance(float overflow);
@@ -392,6 +397,18 @@
method @androidx.compose.runtime.Composable public static void InlineSlider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, int steps, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional boolean segmented, optional kotlin.jvm.functions.Function0<kotlin.Unit> decreaseIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit> increaseIcon, optional androidx.wear.compose.material.InlineSliderColors colors);
}
+ public final class StepperDefaults {
+ method public androidx.compose.ui.graphics.vector.ImageVector getDecreaseIcon();
+ method public androidx.compose.ui.graphics.vector.ImageVector getIncreaseIcon();
+ property public final androidx.compose.ui.graphics.vector.ImageVector DecreaseIcon;
+ property public final androidx.compose.ui.graphics.vector.ImageVector IncreaseIcon;
+ field public static final androidx.wear.compose.material.StepperDefaults INSTANCE;
+ }
+
+ public final class StepperKt {
+ method @androidx.compose.runtime.Composable public static void Stepper(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, int steps, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit> decreaseIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit> increaseIcon, optional long backgroundColor, optional long contentColor, optional long iconTintColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ }
+
@androidx.wear.compose.material.ExperimentalWearMaterialApi public enum SwipeDismissTarget {
enum_constant public static final androidx.wear.compose.material.SwipeDismissTarget Dismissal;
enum_constant public static final androidx.wear.compose.material.SwipeDismissTarget Original;
diff --git a/wear/compose/compose-material/api/restricted_current.txt b/wear/compose/compose-material/api/restricted_current.txt
index a63d79c..8eab278 100644
--- a/wear/compose/compose-material/api/restricted_current.txt
+++ b/wear/compose/compose-material/api/restricted_current.txt
@@ -145,15 +145,15 @@
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues getButtonsContentPadding();
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues getChipsContentPadding();
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues getConfirmationContentPadding();
- method public long getIndefiniteDuration();
- method public long getLongDuration();
- method public long getShortDuration();
+ method public long getIndefiniteDurationMillis();
+ method public long getLongDurationMillis();
+ method public long getShortDurationMillis();
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.PaddingValues ButtonsContentPadding;
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.PaddingValues ChipsContentPadding;
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.PaddingValues ConfirmationContentPadding;
- property public final long IndefiniteDuration;
- property public final long LongDuration;
- property public final long ShortDuration;
+ property public final long IndefiniteDurationMillis;
+ property public final long LongDurationMillis;
+ property public final long ShortDurationMillis;
field public static final androidx.wear.compose.material.DialogDefaults INSTANCE;
}
@@ -178,7 +178,9 @@
public final class InlineSliderDefaults {
method @androidx.compose.runtime.Composable public androidx.wear.compose.material.InlineSliderColors colors(optional long backgroundColor, optional long spacerColor, optional long selectedBarColor, optional long unselectedBarColor, optional long disabledBackgroundColor, optional long disabledSpacerColor, optional long disabledSelectedBarColor, optional long disabledUnselectedBarColor);
method public androidx.compose.ui.graphics.vector.ImageVector getDecreaseIcon();
+ method public androidx.compose.ui.graphics.vector.ImageVector getIncreaseIcon();
property public final androidx.compose.ui.graphics.vector.ImageVector DecreaseIcon;
+ property public final androidx.compose.ui.graphics.vector.ImageVector IncreaseIcon;
field public static final androidx.wear.compose.material.InlineSliderDefaults INSTANCE;
}
@@ -233,7 +235,7 @@
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.wear.compose.material.ScalingLazyListState scalingLazyListState, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.compose.foundation.lazy.LazyListState lazyListState, optional androidx.compose.ui.Modifier modifier, optional boolean reverseDirection);
- method @androidx.compose.runtime.Composable public static void PositionIndicator(float rsbRatio, optional androidx.compose.ui.Modifier modifier, optional long color, optional boolean reverseDirection);
+ method @androidx.compose.runtime.Composable public static void PositionIndicator(kotlin.jvm.functions.Function0<java.lang.Float> value, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> range, optional long color, optional boolean reverseDirection);
method @androidx.compose.runtime.Composable public static void PositionIndicator(androidx.wear.compose.material.PositionIndicatorState state, float indicatorHeight, float indicatorWidth, float paddingRight, optional androidx.compose.ui.Modifier modifier, optional long color, optional long background, optional boolean autoHide, optional boolean reverseDirection);
}
@@ -243,6 +245,9 @@
property public abstract float positionFraction;
}
+ public final class RangeDefaultsKt {
+ }
+
public final class Resources_androidKt {
}
@@ -366,6 +371,18 @@
method @androidx.compose.runtime.Composable public static void InlineSlider(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, int steps, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional boolean segmented, optional kotlin.jvm.functions.Function0<kotlin.Unit> decreaseIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit> increaseIcon, optional androidx.wear.compose.material.InlineSliderColors colors);
}
+ public final class StepperDefaults {
+ method public androidx.compose.ui.graphics.vector.ImageVector getDecreaseIcon();
+ method public androidx.compose.ui.graphics.vector.ImageVector getIncreaseIcon();
+ property public final androidx.compose.ui.graphics.vector.ImageVector DecreaseIcon;
+ property public final androidx.compose.ui.graphics.vector.ImageVector IncreaseIcon;
+ field public static final androidx.wear.compose.material.StepperDefaults INSTANCE;
+ }
+
+ public final class StepperKt {
+ method @androidx.compose.runtime.Composable public static void Stepper(float value, kotlin.jvm.functions.Function1<? super java.lang.Float,kotlin.Unit> onValueChange, int steps, optional androidx.compose.ui.Modifier modifier, optional kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> valueRange, optional kotlin.jvm.functions.Function0<kotlin.Unit> decreaseIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit> increaseIcon, optional long backgroundColor, optional long contentColor, optional long iconTintColor, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ }
+
public final class SwipeToDismissBoxKt {
}
diff --git a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/DialogSample.kt b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/DialogSample.kt
index c5aab1b..7f6b6ed 100644
--- a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/DialogSample.kt
+++ b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/DialogSample.kt
@@ -19,6 +19,8 @@
import androidx.annotation.Sampled
import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -114,7 +116,7 @@
@Sampled
@Composable
fun ConfirmationDialogWithAnimation() {
- val animation = animatedVectorResource(id = R.drawable.open_on_phone_animation)
+ val animation = AnimatedImageVector.animatedVectorResource(R.drawable.open_on_phone_animation)
ConfirmationDialog(
onTimeout = {
/* Do something e.g. navController.popBackStack() */
@@ -129,7 +131,7 @@
onDispose {}
}
Image(
- painter = animation.painterFor(atEnd),
+ painter = rememberAnimatedVectorPainter(animation, atEnd),
contentDescription = "Open on phone",
modifier = Modifier.size(64.dp)
)
diff --git a/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/StepperSample.kt b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/StepperSample.kt
new file mode 100644
index 0000000..61b3ea8
--- /dev/null
+++ b/wear/compose/compose-material/samples/src/main/java/androidx/wear/compose/material/samples/StepperSample.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.compose.material.samples
+
+import androidx.annotation.Sampled
+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.wear.compose.material.Stepper
+import androidx.wear.compose.material.Text
+
+@Sampled
+@Composable
+fun StepperSample() {
+ var value by remember { mutableStateOf(2f) }
+ Stepper(
+ value = value,
+ onValueChange = { value = it },
+ valueRange = 1f..4f,
+ steps = 7
+ ) { Text("Value: $value") }
+}
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PositionIndicatorTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PositionIndicatorTest.kt
index 73fee0d..dd701f6 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PositionIndicatorTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PositionIndicatorTest.kt
@@ -17,15 +17,14 @@
package androidx.wear.compose.material
import androidx.compose.foundation.ScrollState
-import androidx.compose.foundation.gestures.scrollBy
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.requiredSize
-import androidx.compose.ui.Modifier
import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
+import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListLayoutInfo
import androidx.compose.foundation.lazy.LazyListState
@@ -34,6 +33,7 @@
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.test.junit4.createComposeRule
@@ -41,13 +41,13 @@
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.runBlocking
-import org.junit.Ignore
@MediumTest
@RunWith(AndroidJUnit4::class)
@@ -805,7 +805,7 @@
.background(Color.Black),
verticalArrangement = Arrangement.spacedBy(itemSpacingDp),
- ) {
+ ) {
Box(Modifier.requiredSize(itemSizeDp))
Box(Modifier.requiredSize(itemSizeDp))
Box(Modifier.requiredSize(itemSizeDp))
@@ -885,7 +885,7 @@
@Test
fun rsbPositionIndicatorGivesCorrectPositionAndSize() {
val state = mutableStateOf(0f)
- val positionIndicatorState = RsbPositionIndicatorState(state)
+ val positionIndicatorState = FractionPositionIndicatorState { state.value }
rule.setContent {
PositionIndicator(
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/InlineSliderScreenshotTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/SliderScreenshotTest.kt
similarity index 99%
rename from wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/InlineSliderScreenshotTest.kt
rename to wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/SliderScreenshotTest.kt
index 66062fe..1a05807 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/InlineSliderScreenshotTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/SliderScreenshotTest.kt
@@ -39,7 +39,7 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
-class InlineSliderScreenshotTest {
+class SliderScreenshotTest {
@get:Rule
val rule = createComposeRule()
diff --git a/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/Resources.android.kt b/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/Resources.android.kt
index 38b945e..992f1dd 100644
--- a/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/Resources.android.kt
+++ b/wear/compose/compose-material/src/androidMain/kotlin/androidx/wear/compose/material/Resources.android.kt
@@ -27,18 +27,15 @@
internal actual fun isRoundDevice(): Boolean = LocalConfiguration.current.isScreenRound
@Composable
-internal actual fun imageResource(res: String): Painter {
- val id = drawableId(res)
- return painterResource(id)
-}
-
-// TODO: improve resource loading
-private fun drawableId(res: String): Int {
- val imageName = res.substringAfterLast("/").substringBeforeLast(".")
- val drawableClass = R.drawable::class.java
- val field = drawableClass.getDeclaredField(imageName)
- return field.get(drawableClass) as Int
-}
+internal actual fun imageResource(image: ImageResources): Painter =
+ painterResource(
+ when (image) {
+ ImageResources.CircularVignetteBottom -> R.drawable.circular_vignette_bottom
+ ImageResources.CircularVignetteTop -> R.drawable.circular_vignette_top
+ ImageResources.RectangularVignetteBottom -> R.drawable.rectangular_vignette_bottom
+ ImageResources.RectangularVignetteTop -> R.drawable.rectangular_vignette_top
+ }
+ )
@Composable
internal actual fun is24HourFormat(): Boolean = DateFormat.is24HourFormat(LocalContext.current)
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Dialog.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Dialog.kt
index 2916b1d..ec24d8e 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Dialog.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Dialog.kt
@@ -196,8 +196,8 @@
* @param scrollState The scroll state for the dialog so that the scroll position can be displayed
* e.g. by the [PositionIndicator] passed to [Scaffold].
* @param durationMillis The number of milliseconds for which the dialog is displayed,
- * must be positive. Expected to be [DialogDefaults.ShortDuration], [DialogDefaults.LongDuration],
- * or [DialogDefaults.IndefiniteDuration].
+ * must be positive. Suggested values are [DialogDefaults.ShortDurationMillis],
+ * [DialogDefaults.LongDurationMillis] or [DialogDefaults.IndefiniteDurationMillis].
* @param backgroundColor [Color] representing the background color for this dialog.
* @param contentColor [Color] representing the content color for this dialog.
* @param iconTintColor Icon [Color] that defaults to the [contentColor],
@@ -211,7 +211,7 @@
modifier: Modifier = Modifier,
icon: @Composable (() -> Unit)? = null,
scrollState: ScrollState = rememberScrollState(),
- durationMillis: Long = DialogDefaults.ShortDuration,
+ durationMillis: Long = DialogDefaults.ShortDurationMillis,
backgroundColor: Color = MaterialTheme.colors.background,
contentColor: Color = contentColorFor(backgroundColor),
iconTintColor: Color = contentColor,
@@ -286,17 +286,17 @@
/**
* Short duration for showing [ConfirmationDialog].
*/
- val ShortDuration = 4000L
+ val ShortDurationMillis = 4000L
/**
* Long duration for showing [ConfirmationDialog].
*/
- val LongDuration = 10000L
+ val LongDurationMillis = 10000L
/**
* Show [ConfirmationDialog] indefinitely (supports swipe-to-dismiss).
*/
- val IndefiniteDuration = Long.MAX_VALUE
+ val IndefiniteDurationMillis = Long.MAX_VALUE
/**
* Spacing between [Button]s.
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PositionIndicator.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PositionIndicator.kt
index 96f1798d..1625ff2 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PositionIndicator.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/PositionIndicator.kt
@@ -16,7 +16,6 @@
package androidx.wear.compose.material
-import androidx.compose.ui.geometry.lerp
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
@@ -28,24 +27,24 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
-import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.lerp
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import kotlin.math.asin
-import kotlin.math.max
import kotlinx.coroutines.delay
import kotlin.math.PI
import kotlin.math.abs
+import kotlin.math.asin
+import kotlin.math.max
/**
* An object representing the relative position of a scrollbar or rolling side button or rotating
@@ -92,8 +91,9 @@
* [Scroll indicators](https://developer.android.com/training/wearables/components/scroll)
* guide.
*
- * @param scrollState the scrollState to use as the basis for the PositionIndicatorState.
+ * @param scrollState The scrollState to use as the basis for the PositionIndicatorState.
* @param modifier The modifier to be applied to the component
+ * @param reverseDirection Reverses direction of PositionIndicator if true
*/
@Composable
public fun PositionIndicator(
@@ -120,6 +120,7 @@
* @param scalingLazyListState the [ScalingLazyListState] to use as the basis for the
* PositionIndicatorState.
* @param modifier The modifier to be applied to the component
+ * @param reverseDirection Reverses direction of PositionIndicator if true
*/
@Composable
public fun PositionIndicator(
@@ -148,6 +149,7 @@
* @param lazyListState the [LazyListState] to use as the basis for the
* PositionIndicatorState.
* @param modifier The modifier to be applied to the component
+ * @param reverseDirection Reverses direction of PositionIndicator if true
*/
@Composable
public fun PositionIndicator(
@@ -166,26 +168,31 @@
)
/**
- * Creates an [PositionIndicator] for a rotating side button or rotating bezel style indicator.
+ * Creates a [PositionIndicator] for controls like rotating side button, rotating bezel or slider.
*
* For more information, see the
* [Scroll indicators](https://developer.android.com/training/wearables/components/scroll)
* guide.
*
- * @param rsbRatio the value of the rsb/bezel indicator in the range 0..1 where 1 represents the
- * maximum value. E.g. If displaying a volume value say from 0..11 then the rsbRatio would be
+ * @param value Value of the indicator in the [range] where 1 represents the
+ * maximum value. E.g. If displaying a volume value from 0..11 then the [value] will be
* volume/11.
- * @param modifier The modifier to be applied to the component
- * @param color the color to draw the indicator on.
+ * @param range range of values that [value] can take
+ * @param modifier Modifier to be applied to the component
+ * @param color Color to draw the indicator on.
+ * @param reverseDirection Reverses direction of PositionIndicator if true
*/
@Composable
public fun PositionIndicator(
- rsbRatio: Float,
+ value: () -> Float,
modifier: Modifier = Modifier,
+ range: ClosedFloatingPointRange<Float> = 0f..1f,
color: Color = MaterialTheme.colors.onBackground,
reverseDirection: Boolean = false
) = PositionIndicator(
- state = RsbPositionIndicatorState(remember { mutableStateOf(rsbRatio) }),
+ state = FractionPositionIndicatorState {
+ (value() - range.start) / (range.endInclusive - range.start)
+ },
indicatorHeight = 76.dp,
indicatorWidth = 6.dp,
paddingRight = 5.dp,
@@ -223,6 +230,7 @@
* @param color the color to draw the active part of the indicator in
* @param background the color to draw the non-active part of the position indicator.
* @param autoHide whether the indicator should be automatically hidden after showing the change in
+ * @param reverseDirection Reverses direction of PositionIndicator if true
*/
@Composable
public fun PositionIndicator(
@@ -305,27 +313,26 @@
}
/**
- * An implementation of [PositionIndicatorState] to display the a value that is being incremented or
- * decremented with a rolling side button or rotating bezel, e.g. A volume control.
+ * An implementation of [PositionIndicatorState] to display a value that is being incremented or
+ * decremented with a rolling side button, rotating bezel or a slider e.g. a volume control.
*
- * @param rsbRatio the value of the rsb/bezel indicator in the range 0..1 where 1 represents the
- * maximum value. E.g. If displaying a volume value say from 0..11 then the rsbRatio would be
+ * @param fraction Value of the indicator in the range 0..1 where 1 represents the
+ * maximum value. E.g. If displaying a volume value from 0..11 then the [fraction] will be
* volume/11.
*
* @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
*/
-internal class RsbPositionIndicatorState(
- private val rsbRatio: State<Float>
+internal class FractionPositionIndicatorState(
+ private val fraction: () -> Float
) : PositionIndicatorState {
- override val positionFraction = 1f
+ override val positionFraction = 1f // Position indicator always starts at the bottom|end
- override fun sizeFraction(scrollableContainerSizePx: Float) = rsbRatio.value
+ override fun sizeFraction(scrollableContainerSizePx: Float) = fraction()
- override fun equals(other: Any?) = (other as? RsbPositionIndicatorState)?.rsbRatio == rsbRatio
+ override fun equals(other: Any?) =
+ (other as? FractionPositionIndicatorState)?.fraction?.invoke() == fraction()
- override fun hashCode(): Int {
- return rsbRatio.hashCode()
- }
+ override fun hashCode(): Int = fraction().hashCode()
}
/**
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/RangeDefaults.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/RangeDefaults.kt
new file mode 100644
index 0000000..706cc65
--- /dev/null
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/RangeDefaults.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.compose.material
+
+import androidx.compose.foundation.progressSemantics
+import androidx.compose.material.icons.materialIcon
+import androidx.compose.material.icons.materialPath
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.semantics.disabled
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.setProgress
+import androidx.compose.ui.util.lerp
+import kotlin.math.roundToInt
+
+/**
+ * Icons which are used by Range controls like slider and stepper
+ */
+internal object RangeIcons {
+
+ /**
+ * An [Icon] with a minus sign.
+ */
+ val MinusIcon: ImageVector
+ get() = if (_minusIcon != null) _minusIcon!!
+ else {
+ _minusIcon = materialIcon(name = "MinusIcon") {
+ materialPath {
+ moveTo(19.0f, 13.0f)
+ horizontalLineTo(5.0f)
+ verticalLineToRelative(-2.0f)
+ horizontalLineToRelative(14.0f)
+ verticalLineToRelative(2.0f)
+ close()
+ }
+ }
+ _minusIcon!!
+ }
+
+ private var _minusIcon: ImageVector? = null
+}
+
+/**
+ * Defaults used by range controls like slider and stepper
+ */
+internal object RangeDefaults {
+ /**
+ * Calculates value of [currentStep] in [valueRange] depending on number of [steps]
+ */
+ fun calculateCurrentStepValue(
+ currentStep: Int,
+ steps: Int,
+ valueRange: ClosedFloatingPointRange<Float>
+ ): Float = lerp(
+ valueRange.start, valueRange.endInclusive,
+ currentStep.toFloat() / (steps + 1).toFloat()
+ ).coerceIn(valueRange)
+
+ /**
+ * Snaps [value] to the closest [step] in the [valueRange]
+ */
+ fun snapValueToStep(
+ value: Float,
+ valueRange: ClosedFloatingPointRange<Float>,
+ steps: Int
+ ): Int = ((value - valueRange.start) /
+ (valueRange.endInclusive - valueRange.start) * (steps + 1))
+ .roundToInt().coerceIn(0, steps + 1)
+}
+
+internal fun Modifier.rangeSemantics(
+ step: Int,
+ enabled: Boolean,
+ onValueChange: (Float) -> Unit,
+ valueRange: ClosedFloatingPointRange<Float>,
+ steps: Int
+): Modifier = semantics(mergeDescendants = true) {
+ if (!enabled) disabled()
+ setProgress(
+ action = { targetValue ->
+ val newStepIndex = RangeDefaults.snapValueToStep(targetValue, valueRange, steps)
+ if (step == newStepIndex) {
+ false
+ } else {
+ onValueChange(targetValue)
+ true
+ }
+ }
+ )
+}.progressSemantics(
+ RangeDefaults.calculateCurrentStepValue(step, steps, valueRange),
+ valueRange, steps
+)
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Resources.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Resources.kt
index e5a01e9..8891cd3 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Resources.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Resources.kt
@@ -19,8 +19,15 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
+internal enum class ImageResources {
+ CircularVignetteBottom,
+ CircularVignetteTop,
+ RectangularVignetteBottom,
+ RectangularVignetteTop,
+}
+
@Composable
-internal expect fun imageResource(res: String): Painter
+internal expect fun imageResource(image: ImageResources): Painter
@Composable
internal expect fun isRoundDevice(): Boolean
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Slider.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Slider.kt
index 3877a46..b8a52fc 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Slider.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Slider.kt
@@ -28,11 +28,8 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.progressSemantics
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
-import androidx.compose.material.icons.materialIcon
-import androidx.compose.material.icons.materialPath
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
@@ -48,13 +45,10 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.semantics.disabled
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.semantics.setProgress
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.lerp
-import kotlin.math.roundToInt
+import androidx.wear.compose.material.RangeDefaults.calculateCurrentStepValue
+import androidx.wear.compose.material.RangeDefaults.snapValueToStep
/**
* [InlineSlider] allows users to make a selection from a range of values. The range of selections
@@ -66,9 +60,9 @@
* accordingly to the start and end of the control. Buttons can have custom icons -
* [decreaseIcon] and [increaseIcon].
*
- * The selection bar in the middle of control can have separators if [segmented] flag is set to true.
+ * The bar in the middle of control can have separators if [segmented] flag is set to true.
* A single step value is calculated as the difference between min and max values of [valueRange]
- * divided by [steps] value.
+ * divided by [steps] + 1 value.
*
* A continuous non-segmented slider sample:
* @sample androidx.wear.compose.material.samples.InlineSliderSample
@@ -112,7 +106,7 @@
},
increaseIcon: @Composable () -> Unit = {
Icon(
- imageVector = Icons.Filled.Add,
+ imageVector = InlineSliderDefaults.IncreaseIcon,
contentDescription = "Increase" // TODO(b/204187777) i18n
)
},
@@ -124,7 +118,7 @@
Box(
modifier = modifier
.fillMaxWidth()
- .inlineSliderSemantics(
+ .rangeSemantics(
currentStep,
enabled,
onValueChange,
@@ -150,7 +144,7 @@
horizontalArrangement = Arrangement.Start,
modifier = Modifier.fillMaxWidth().background(backgroundColor.value)
) {
- ActionButton(
+ InlineSliderButton(
enabled = enabled,
onClick = { updateValue(-1) },
contentAlignment = Alignment.CenterStart,
@@ -189,7 +183,7 @@
.background(colors.spacerColor(enabled).value)
)
- ActionButton(
+ InlineSliderButton(
enabled = enabled,
onClick = { updateValue(1) },
contentAlignment = Alignment.CenterEnd,
@@ -233,7 +227,7 @@
}
/**
- * Defaults used by [InlineSlider]
+ * Defaults used by slider
*/
object InlineSliderDefaults {
@@ -291,25 +285,16 @@
)
/**
- * An [Icon] with a minus sign.
+ * A decrease [Icon].
*/
val DecreaseIcon: ImageVector
- get() = if (_decreaseIcon != null) _decreaseIcon!!
- else {
- _decreaseIcon = materialIcon(name = "DecreaseIcon") {
- materialPath {
- moveTo(19.0f, 13.0f)
- horizontalLineTo(5.0f)
- verticalLineToRelative(-2.0f)
- horizontalLineToRelative(14.0f)
- verticalLineToRelative(2.0f)
- close()
- }
- }
- _decreaseIcon!!
- }
+ get() = RangeIcons.MinusIcon
- private var _decreaseIcon: ImageVector? = null
+ /**
+ * An increase [Icon].
+ */
+ val IncreaseIcon: ImageVector
+ get() = Icons.Filled.Add
}
@Immutable
@@ -375,7 +360,7 @@
}
@Composable
-private fun ActionButton(
+private fun InlineSliderButton(
enabled: Boolean,
onClick: () -> Unit,
contentAlignment: Alignment,
@@ -397,43 +382,6 @@
}
}
-private fun calculateCurrentStepValue(
- currentStep: Int,
- steps: Int,
- valueRange: ClosedFloatingPointRange<Float>
-): Float = lerp(
- valueRange.start, valueRange.endInclusive,
- currentStep.toFloat() / (steps + 1).toFloat()
-).coerceIn(valueRange)
-
-private fun snapValueToStep(
- value: Float,
- valueRange: ClosedFloatingPointRange<Float>,
- steps: Int
-): Int = ((value - valueRange.start) / (valueRange.endInclusive - valueRange.start) * (steps + 1))
- .roundToInt().coerceIn(0, steps + 1)
-
-private fun Modifier.inlineSliderSemantics(
- step: Int,
- enabled: Boolean,
- onValueChange: (Float) -> Unit,
- valueRange: ClosedFloatingPointRange<Float>,
- steps: Int
-): Modifier = semantics(mergeDescendants = true) {
- if (!enabled) disabled()
- setProgress(
- action = { targetValue ->
- val newStepIndex = snapValueToStep(targetValue, valueRange, steps)
- if (step == newStepIndex) {
- false
- } else {
- onValueChange(targetValue)
- true
- }
- }
- )
-}.progressSemantics(calculateCurrentStepValue(step, steps, valueRange), valueRange, steps)
-
private fun Modifier.drawProgressBar(
selectedBarColor: State<Color>,
unselectedBarColor: State<Color>,
@@ -474,4 +422,4 @@
}
private fun <T> directedValue(layoutDirection: LayoutDirection, ltrValue: T, rtlValue: T): T =
- if (layoutDirection == LayoutDirection.Ltr) ltrValue else rtlValue
\ No newline at end of file
+ if (layoutDirection == LayoutDirection.Ltr) ltrValue else rtlValue
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Stepper.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Stepper.kt
new file mode 100644
index 0000000..fa71cb1
--- /dev/null
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Stepper.kt
@@ -0,0 +1,192 @@
+/*
+ * 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.compose.material
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.indication
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.RangeDefaults.calculateCurrentStepValue
+import androidx.wear.compose.material.RangeDefaults.snapValueToStep
+
+/**
+ * [Stepper] allows users to make a selection from a range of values.
+ * It's a full-screen control with increase button on the top, decrease button on the bottom and
+ * a slot (expected to have either [Text] or [Chip]) in the middle.
+ * Value can be increased and decreased by clicking on the increase and decrease buttons.
+ * Buttons can have custom icons - [decreaseIcon] and [increaseIcon].
+ * Step value is calculated as the difference between min and max values divided by [steps]+1.
+ * Stepper itself doesn't show the current value but can be displayed via the content slot or
+ * [PositionIndicator] if required.
+ *
+ * @sample androidx.wear.compose.material.samples.StepperSample
+ *
+ * @param value Current value of the Stepper. If outside of [valueRange] provided, value will be
+ * coerced to this range.
+ * @param onValueChange Lambda in which value should be updated
+ * @param steps Specifies the number of discrete values, evenly distributed
+ * between across the whole value range. Must not be negative. If 0, stepper will have only
+ * min and max values and no steps in between
+ * @param modifier Modifiers for the Stepper layout
+ * @param valueRange Range of values that Stepper value can take. Passed [value] will be coerced to
+ * this range
+ * @param decreaseIcon A slot for an icon which is placed on the decrease (bottom) button
+ * @param increaseIcon A slot for an icon which is placed on the increase (top) button
+ * @param backgroundColor [Color] representing the background color for the stepper.
+ * @param contentColor [Color] representing the color for [content] in the middle.
+ * @param iconTintColor Icon tint [Color] which used by [increaseIcon] and [decreaseIcon]
+ * that defaults to [contentColor], unless specifically overridden.
+ */
+@Composable
+fun Stepper(
+ value: Float,
+ onValueChange: (Float) -> Unit,
+ steps: Int,
+ modifier: Modifier = Modifier,
+ valueRange: ClosedFloatingPointRange<Float> = 0f..(steps + 1).toFloat(),
+ decreaseIcon: @Composable () -> Unit = {
+ Icon(
+ imageVector = StepperDefaults.DecreaseIcon,
+ contentDescription = "Decrease", // TODO(b/204187777) i18n
+ modifier = Modifier.padding(14.dp)
+ )
+ },
+ increaseIcon: @Composable () -> Unit = {
+ Icon(
+ imageVector = StepperDefaults.IncreaseIcon,
+ contentDescription = "Increase", // TODO(b/204187777) i18n
+ modifier = Modifier.padding(14.dp)
+ )
+ },
+ backgroundColor: Color = MaterialTheme.colors.background,
+ contentColor: Color = contentColorFor(backgroundColor),
+ iconTintColor: Color = contentColor,
+ content: @Composable BoxScope.() -> Unit
+) {
+ require(steps >= 0) { "steps should be >= 0" }
+ val currentStep =
+ remember(value, valueRange, steps) { snapValueToStep(value, valueRange, steps) }
+
+ val updateValue: (Int) -> Unit = { stepDiff ->
+ val newValue = calculateCurrentStepValue(currentStep + stepDiff, steps, valueRange)
+ if (newValue != value) onValueChange(newValue)
+ }
+
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .background(backgroundColor)
+ .rangeSemantics(
+ currentStep,
+ true,
+ onValueChange,
+ valueRange,
+ steps
+ ),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ FullScreenButton(
+ onClick = { updateValue(1) },
+ contentAlignment = Alignment.TopCenter,
+ paddingValues = PaddingValues(top = StepperDefaults.BorderPadding),
+ iconTintColor = iconTintColor,
+ content = increaseIcon
+ )
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(StepperDefaults.ContentWeight),
+ contentAlignment = Alignment.Center,
+ ) {
+ CompositionLocalProvider(LocalContentColor provides contentColor) {
+ content()
+ }
+ }
+ FullScreenButton(
+ onClick = { updateValue(-1) },
+ contentAlignment = Alignment.BottomCenter,
+ paddingValues = PaddingValues(bottom = StepperDefaults.BorderPadding),
+ content = decreaseIcon,
+ iconTintColor = iconTintColor
+ )
+ }
+}
+
+@Composable
+private fun ColumnScope.FullScreenButton(
+ onClick: () -> Unit,
+ contentAlignment: Alignment,
+ paddingValues: PaddingValues,
+ iconTintColor: Color,
+ content: @Composable () -> Unit
+) {
+ val interactionSource = remember { MutableInteractionSource() }
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(StepperDefaults.ButtonWeight)
+ .clickable(interactionSource, null, onClick = onClick)
+ .wrapContentWidth()
+ .indication(interactionSource, rememberRipple(bounded = false))
+ .padding(paddingValues),
+ contentAlignment = contentAlignment,
+ ) {
+ CompositionLocalProvider(LocalContentColor provides iconTintColor, content = content)
+ }
+}
+
+/**
+ * Defaults used by stepper
+ */
+object StepperDefaults {
+ internal const val ButtonWeight = 0.35f
+ internal const val ContentWeight = 0.3f
+ internal val BorderPadding = 8.dp
+
+ /**
+ * A decrease [Icon].
+ */
+ val DecreaseIcon: ImageVector
+ get() = RangeIcons.MinusIcon
+
+ /**
+ * An increase [Icon].
+ */
+ val IncreaseIcon: ImageVector
+ get() = Icons.Filled.Add
+}
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Vignette.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Vignette.kt
index 2f19da1..568f715 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Vignette.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Vignette.kt
@@ -98,8 +98,8 @@
if (vignettePosition.drawTop()) {
Image(
painter = imageResource(
- if (isRoundDevice()) "circular_vignette_top"
- else "rectangular_vignette_top"
+ if (isRoundDevice()) ImageResources.CircularVignetteTop
+ else ImageResources.RectangularVignetteTop
),
contentScale = ContentScale.FillWidth,
contentDescription = null,
@@ -109,8 +109,8 @@
if (vignettePosition.drawBottom()) {
Image(
painter = imageResource(
- if (isRoundDevice()) "circular_vignette_bottom"
- else "rectangular_vignette_bottom"
+ if (isRoundDevice()) ImageResources.CircularVignetteBottom
+ else ImageResources.RectangularVignetteBottom
),
contentScale = ContentScale.FillWidth,
contentDescription = null,
diff --git a/wear/compose/compose-material/src/desktopMain/kotlin/androidx/wear/compose/material/Resources.desktop.kt b/wear/compose/compose-material/src/desktopMain/kotlin/androidx/wear/compose/material/Resources.desktop.kt
index 003eca7..8b15984 100644
--- a/wear/compose/compose-material/src/desktopMain/kotlin/androidx/wear/compose/material/Resources.desktop.kt
+++ b/wear/compose/compose-material/src/desktopMain/kotlin/androidx/wear/compose/material/Resources.desktop.kt
@@ -26,8 +26,15 @@
}
@Composable
-internal actual fun imageResource(res: String): Painter =
- painterResource(res)
+internal actual fun imageResource(image: ImageResources): Painter =
+ painterResource(
+ when (image) {
+ ImageResources.CircularVignetteBottom -> "circular_vignette_bottom"
+ ImageResources.CircularVignetteTop -> "circular_vignette_top"
+ ImageResources.RectangularVignetteBottom -> "rectangular_vignette_bottom"
+ ImageResources.RectangularVignetteTop -> "rectangular_vignette_top"
+ }
+ )
@Composable
internal actual fun is24HourFormat(): Boolean {
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
index 4d38b8f..2049ef7 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/MaterialDemos.kt
@@ -40,6 +40,7 @@
import androidx.wear.compose.material.samples.SimpleSwipeToDismissBox
import androidx.wear.compose.material.samples.SplitToggleChipWithCheckbox
import androidx.wear.compose.material.samples.StatefulSwipeToDismissBox
+import androidx.wear.compose.material.samples.StepperSample
import androidx.wear.compose.material.samples.TimeTextWithCustomSeparator
import androidx.wear.compose.material.samples.TimeTextWithFullDateAndTimeFormat
import androidx.wear.compose.material.samples.TitleCardStandard
@@ -105,6 +106,31 @@
)
),
DemoCategory(
+ "Stepper",
+ listOf(
+ DemoCategory(
+ "Samples",
+ listOf(
+ ComposableDemo("Stepper") {
+ Centralize({ StepperSample() })
+ },
+ )
+ ),
+ DemoCategory(
+ "Demos",
+ listOf(
+ ComposableDemo("Simple stepper") { StepperDemo() },
+ ComposableDemo("With scrollbar") {
+ StepperWithScrollBarDemo()
+ },
+ ComposableDemo("With custom colors") {
+ StepperWithCustomColors()
+ },
+ )
+ )
+ )
+ ),
+ DemoCategory(
"TimeText",
listOf(
DemoCategory(
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 768565c..1118ea7 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
@@ -122,4 +122,4 @@
steps = numberOfSegments.toInt()
)
}
-}
\ No newline at end of file
+}
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/StepperDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/StepperDemo.kt
new file mode 100644
index 0000000..cbde90a
--- /dev/null
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/StepperDemo.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.compose.integration.demos
+
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.Chip
+import androidx.wear.compose.material.ChipDefaults
+import androidx.wear.compose.material.Icon
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.Stepper
+import androidx.wear.compose.material.Text
+
+@Composable
+fun StepperDemo() {
+ var value by remember { mutableStateOf(2f) }
+ Stepper(
+ value = value,
+ onValueChange = { value = it },
+ valueRange = 1f..4f,
+ steps = 2
+ ) { Text("Value: $value") }
+}
+
+@Composable
+fun StepperWithScrollBarDemo() {
+ val valueState = remember { mutableStateOf(4f) }
+ val range = 0f..10f
+
+ Stepper(
+ value = valueState.value,
+ onValueChange = {
+ valueState.value = it
+ },
+ valueRange = range,
+ steps = 9
+ ) {
+ Chip(
+ onClick = { valueState.value = if (valueState.value == 0f) 4f else 0f },
+ modifier = Modifier.width(146.dp),
+ colors = ChipDefaults.secondaryChipColors(),
+ label = { Text("Volume : ${valueState.value}") },
+ icon = {
+ Icon(
+ painter = painterResource(
+ id = if (valueState.value > 0)
+ R.drawable.ic_volume_up_24px
+ else R.drawable.ic_volume_off_24px
+ ),
+ contentDescription = null,
+ )
+ }
+ )
+ }
+
+ PositionIndicator(
+ value = { valueState.value },
+ range = range
+ )
+}
+
+@Composable
+fun StepperWithCustomColors() {
+ val valueState = remember { mutableStateOf(4f) }
+ val range = 0f..10f
+
+ Stepper(
+ value = valueState.value,
+ onValueChange = {
+ valueState.value = it
+ },
+ valueRange = range,
+ steps = 9,
+ backgroundColor = Color.Blue,
+ contentColor = Color.Red,
+ iconTintColor = Color.Green
+ ) {
+ Text("Volume : ${valueState.value}")
+ }
+
+ PositionIndicator(
+ value = { valueState.value },
+ range = range
+ )
+}
diff --git a/wear/watchface/watchface-client-guava/api/current.txt b/wear/watchface/watchface-client-guava/api/current.txt
index 5115180..d17f246 100644
--- a/wear/watchface/watchface-client-guava/api/current.txt
+++ b/wear/watchface/watchface-client-guava/api/current.txt
@@ -18,5 +18,14 @@
method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
}
+ public final class ListenableWatchFaceMetadataClient {
+ method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+ field public static final androidx.wear.watchface.client.ListenableWatchFaceMetadataClient.Companion Companion;
+ }
+
+ public static final class ListenableWatchFaceMetadataClient.Companion {
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+ }
+
}
diff --git a/wear/watchface/watchface-client-guava/api/public_plus_experimental_current.txt b/wear/watchface/watchface-client-guava/api/public_plus_experimental_current.txt
index 030cf28..d17f246 100644
--- a/wear/watchface/watchface-client-guava/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-client-guava/api/public_plus_experimental_current.txt
@@ -18,14 +18,13 @@
method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
}
- @androidx.wear.watchface.client.WatchFaceClientExperimental public final class ListenableWatchFaceMetadataClient {
- ctor public ListenableWatchFaceMetadataClient();
- method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> createListenableWatchFaceMetadataClient(android.content.Context context, android.content.ComponentName watchFaceName);
+ public final class ListenableWatchFaceMetadataClient {
+ method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
field public static final androidx.wear.watchface.client.ListenableWatchFaceMetadataClient.Companion Companion;
}
public static final class ListenableWatchFaceMetadataClient.Companion {
- method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> createListenableWatchFaceMetadataClient(android.content.Context context, android.content.ComponentName watchFaceName);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
}
}
diff --git a/wear/watchface/watchface-client-guava/api/restricted_current.txt b/wear/watchface/watchface-client-guava/api/restricted_current.txt
index 5115180..d17f246 100644
--- a/wear/watchface/watchface-client-guava/api/restricted_current.txt
+++ b/wear/watchface/watchface-client-guava/api/restricted_current.txt
@@ -18,5 +18,14 @@
method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
}
+ public final class ListenableWatchFaceMetadataClient {
+ method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+ field public static final androidx.wear.watchface.client.ListenableWatchFaceMetadataClient.Companion Companion;
+ }
+
+ public static final class ListenableWatchFaceMetadataClient.Companion {
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+ }
+
}
diff --git a/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceMetadataClientTest.kt b/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceMetadataClientTest.kt
index aac5ad0..9dca287 100644
--- a/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceMetadataClientTest.kt
+++ b/wear/watchface/watchface-client-guava/src/androidTest/java/androidx/wear/watchface/client/guava/ListenableWatchFaceMetadataClientTest.kt
@@ -29,7 +29,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.wear.watchface.client.ListenableWatchFaceMetadataClient
-import androidx.wear.watchface.client.WatchFaceClientExperimental
import androidx.wear.watchface.client.WatchFaceMetadataClient
import androidx.wear.watchface.control.IWatchFaceInstanceServiceStub
import androidx.wear.watchface.control.WatchFaceControlService
@@ -61,7 +60,6 @@
@RunWith(AndroidJUnit4::class)
@MediumTest
-@OptIn(WatchFaceClientExperimental::class)
public class ListenableWatchFaceMetadataClientTest {
private val exampleWatchFaceComponentName = ComponentName(
"androidx.wear.watchface.samples.test",
@@ -73,7 +71,7 @@
@Test
public fun getSchema() {
val listenableFuture =
- ListenableWatchFaceMetadataClient.createListenableWatchFaceMetadataClientImpl(
+ ListenableWatchFaceMetadataClient.createImpl(
context,
Intent(context, WatchFaceControlTestService::class.java).apply {
action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
diff --git a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt
index d1f4dbb..5799282 100644
--- a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt
+++ b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt
@@ -182,6 +182,7 @@
override fun getEditorServiceClient(): EditorServiceClient =
watchFaceControlClient.getEditorServiceClient()
+ @Suppress("DEPRECATION")
override fun getDefaultComplicationDataSourcePoliciesAndType(
watchFaceName: ComponentName
): Map<Int, DefaultComplicationDataSourcePolicyAndType> =
diff --git a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt
index 66c2ee8..6a1d13b 100644
--- a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt
+++ b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt
@@ -25,11 +25,9 @@
import com.google.common.util.concurrent.ListenableFuture
/**
- * [ListenableFuture]-based compatibility wrapper around
- * [WatchFaceMetadataClient.createWatchFaceMetadataClient].
+ * [ListenableFuture]-based compatibility wrapper around [WatchFaceMetadataClient.create].
*/
-@WatchFaceClientExperimental
-public class ListenableWatchFaceMetadataClient {
+public class ListenableWatchFaceMetadataClient private constructor() {
public companion object {
/**
* Constructs a [WatchFaceMetadataClient] for fetching metadata for the specified watch
@@ -44,20 +42,19 @@
*/
@Suppress("AsyncSuffixFuture")
@JvmStatic
- public fun createListenableWatchFaceMetadataClient(
+ public fun create(
context: Context,
watchFaceName: ComponentName
- ): ListenableFuture<WatchFaceMetadataClient> =
- createListenableWatchFaceMetadataClientImpl(
- context,
- Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
- setPackage(watchFaceName.packageName)
- },
- watchFaceName,
- WatchFaceMetadataClient.Companion.ParserProvider()
- )
+ ): ListenableFuture<WatchFaceMetadataClient> = createImpl(
+ context,
+ Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
+ setPackage(watchFaceName.packageName)
+ },
+ watchFaceName,
+ WatchFaceMetadataClient.Companion.ParserProvider()
+ )
- internal fun createListenableWatchFaceMetadataClientImpl(
+ internal fun createImpl(
context: Context,
intent: Intent,
watchFaceName: ComponentName,
@@ -65,7 +62,7 @@
) = ListenableWatchFaceControlClient.launchFutureCoroutine(
"ListenableWatchFaceMetadataClient.listenableCreateWatchFaceMetadataClient"
) {
- WatchFaceMetadataClient.createWatchFaceMetadataClientImpl(
+ WatchFaceMetadataClient.createImpl(
context,
intent,
watchFaceName,
diff --git a/wear/watchface/watchface-client/api/current.txt b/wear/watchface/watchface-client/api/current.txt
index ae220e2..421504b 100644
--- a/wear/watchface/watchface-client/api/current.txt
+++ b/wear/watchface/watchface-client/api/current.txt
@@ -1,14 +1,33 @@
// Signature format: 4.0
package androidx.wear.watchface.client {
+ public final class ComplicationSlotMetadata {
+ ctor public ComplicationSlotMetadata(androidx.wear.watchface.complications.ComplicationSlotBounds? bounds, int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, boolean isInitiallyEnabled, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
+ method public androidx.wear.watchface.complications.ComplicationSlotBounds? getBounds();
+ method public int getBoundsType();
+ method public android.os.Bundle getComplicationConfigExtras();
+ method public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getDefaultDataSourcePolicy();
+ method public boolean getFixedComplicationDataSource();
+ method public java.util.List<androidx.wear.watchface.complications.data.ComplicationType> getSupportedTypes();
+ method public boolean isInitiallyEnabled();
+ property public final androidx.wear.watchface.complications.ComplicationSlotBounds? bounds;
+ property public final int boundsType;
+ property public final android.os.Bundle complicationConfigExtras;
+ property public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy;
+ property public final boolean fixedComplicationDataSource;
+ property public final boolean isInitiallyEnabled;
+ property public final java.util.List<androidx.wear.watchface.complications.data.ComplicationType> supportedTypes;
+ }
+
public final class ComplicationSlotState {
- ctor public ComplicationSlotState(android.graphics.Rect bounds, int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType, boolean isEnabled, boolean isInitiallyEnabled, androidx.wear.watchface.complications.data.ComplicationType currentType, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
+ ctor public ComplicationSlotState(android.graphics.Rect bounds, int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, boolean isEnabled, boolean isInitiallyEnabled, androidx.wear.watchface.complications.data.ComplicationType currentType, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
+ ctor @Deprecated public ComplicationSlotState(android.graphics.Rect bounds, int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType, boolean isEnabled, boolean isInitiallyEnabled, androidx.wear.watchface.complications.data.ComplicationType currentType, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
method public android.graphics.Rect getBounds();
method public int getBoundsType();
method public android.os.Bundle getComplicationConfigExtras();
method public androidx.wear.watchface.complications.data.ComplicationType getCurrentType();
method public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getDefaultDataSourcePolicy();
- method public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
+ method @Deprecated public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
method public boolean getFixedComplicationDataSource();
method public java.util.List<androidx.wear.watchface.complications.data.ComplicationType> getSupportedTypes();
method public boolean isEnabled();
@@ -18,17 +37,17 @@
property public final android.os.Bundle complicationConfigExtras;
property public final androidx.wear.watchface.complications.data.ComplicationType currentType;
property public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy;
- property public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
+ property @Deprecated public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
property public final boolean fixedComplicationDataSource;
property public final boolean isEnabled;
property public final boolean isInitiallyEnabled;
property public final java.util.List<androidx.wear.watchface.complications.data.ComplicationType> supportedTypes;
}
- public final class DefaultComplicationDataSourcePolicyAndType {
- ctor public DefaultComplicationDataSourcePolicyAndType(androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy policy, androidx.wear.watchface.complications.data.ComplicationType type);
- method public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getPolicy();
- method public androidx.wear.watchface.complications.data.ComplicationType getType();
+ @Deprecated public final class DefaultComplicationDataSourcePolicyAndType {
+ ctor @Deprecated public DefaultComplicationDataSourcePolicyAndType(androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy policy, androidx.wear.watchface.complications.data.ComplicationType type);
+ method @Deprecated public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getPolicy();
+ method @Deprecated public androidx.wear.watchface.complications.data.ComplicationType getType();
property public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy policy;
property public final androidx.wear.watchface.complications.data.ComplicationType type;
}
@@ -147,7 +166,7 @@
public interface WatchFaceControlClient extends java.lang.AutoCloseable {
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public default static suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient> p) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
- method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName) throws android.os.RemoteException;
+ method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient() throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public suspend Object? getOrCreateInteractiveWatchFaceClient(String id, androidx.wear.watchface.client.DeviceConfig deviceConfig, androidx.wear.watchface.client.WatchUiState watchUiState, androidx.wear.watchface.style.UserStyleData? userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.watchface.complications.data.ComplicationData>? slotIdToComplicationData, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.InteractiveWatchFaceClient> p) throws android.os.RemoteException;
@@ -172,6 +191,24 @@
property public final String id;
}
+ public interface WatchFaceMetadataClient extends java.lang.AutoCloseable {
+ method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationSlotMetadata> getComplicationSlotMetadataMap();
+ method public androidx.wear.watchface.style.UserStyleSchema getUserStyleSchema();
+ field public static final androidx.wear.watchface.client.WatchFaceMetadataClient.Companion Companion;
+ }
+
+ public static final class WatchFaceMetadataClient.Companion {
+ method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? create(android.content.Context context, android.content.ComponentName watchFaceName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient> p) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
+ }
+
+ public static final class WatchFaceMetadataClient.ServiceNotBoundException extends java.lang.Exception {
+ ctor public WatchFaceMetadataClient.ServiceNotBoundException();
+ }
+
+ public static final class WatchFaceMetadataClient.ServiceStartFailureException extends java.lang.Exception {
+ ctor public WatchFaceMetadataClient.ServiceStartFailureException(optional String message);
+ }
+
public final class WatchUiState {
ctor public WatchUiState(boolean inAmbientMode, int interruptionFilter);
method public boolean getInAmbientMode();
diff --git a/wear/watchface/watchface-client/api/public_plus_experimental_current.txt b/wear/watchface/watchface-client/api/public_plus_experimental_current.txt
index 95b4f76..15c1cc1 100644
--- a/wear/watchface/watchface-client/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-client/api/public_plus_experimental_current.txt
@@ -1,13 +1,12 @@
// Signature format: 4.0
package androidx.wear.watchface.client {
- @androidx.wear.watchface.client.WatchFaceClientExperimental public final class ComplicationSlotMetadata {
- ctor public ComplicationSlotMetadata(androidx.wear.watchface.complications.ComplicationSlotBounds? bounds, int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType, boolean isInitiallyEnabled, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
+ public final class ComplicationSlotMetadata {
+ ctor public ComplicationSlotMetadata(androidx.wear.watchface.complications.ComplicationSlotBounds? bounds, int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, boolean isInitiallyEnabled, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
method public androidx.wear.watchface.complications.ComplicationSlotBounds? getBounds();
method public int getBoundsType();
method public android.os.Bundle getComplicationConfigExtras();
method public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getDefaultDataSourcePolicy();
- method public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
method public boolean getFixedComplicationDataSource();
method public java.util.List<androidx.wear.watchface.complications.data.ComplicationType> getSupportedTypes();
method public boolean isInitiallyEnabled();
@@ -15,20 +14,20 @@
property public final int boundsType;
property public final android.os.Bundle complicationConfigExtras;
property public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy;
- property public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
property public final boolean fixedComplicationDataSource;
property public final boolean isInitiallyEnabled;
property public final java.util.List<androidx.wear.watchface.complications.data.ComplicationType> supportedTypes;
}
public final class ComplicationSlotState {
- ctor public ComplicationSlotState(android.graphics.Rect bounds, int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType, boolean isEnabled, boolean isInitiallyEnabled, androidx.wear.watchface.complications.data.ComplicationType currentType, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
+ ctor public ComplicationSlotState(android.graphics.Rect bounds, int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, boolean isEnabled, boolean isInitiallyEnabled, androidx.wear.watchface.complications.data.ComplicationType currentType, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
+ ctor @Deprecated public ComplicationSlotState(android.graphics.Rect bounds, int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType, boolean isEnabled, boolean isInitiallyEnabled, androidx.wear.watchface.complications.data.ComplicationType currentType, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
method public android.graphics.Rect getBounds();
method public int getBoundsType();
method public android.os.Bundle getComplicationConfigExtras();
method public androidx.wear.watchface.complications.data.ComplicationType getCurrentType();
method public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getDefaultDataSourcePolicy();
- method public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
+ method @Deprecated public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
method public boolean getFixedComplicationDataSource();
method public java.util.List<androidx.wear.watchface.complications.data.ComplicationType> getSupportedTypes();
method public boolean isEnabled();
@@ -38,17 +37,17 @@
property public final android.os.Bundle complicationConfigExtras;
property public final androidx.wear.watchface.complications.data.ComplicationType currentType;
property public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy;
- property public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
+ property @Deprecated public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
property public final boolean fixedComplicationDataSource;
property public final boolean isEnabled;
property public final boolean isInitiallyEnabled;
property public final java.util.List<androidx.wear.watchface.complications.data.ComplicationType> supportedTypes;
}
- public final class DefaultComplicationDataSourcePolicyAndType {
- ctor public DefaultComplicationDataSourcePolicyAndType(androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy policy, androidx.wear.watchface.complications.data.ComplicationType type);
- method public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getPolicy();
- method public androidx.wear.watchface.complications.data.ComplicationType getType();
+ @Deprecated public final class DefaultComplicationDataSourcePolicyAndType {
+ ctor @Deprecated public DefaultComplicationDataSourcePolicyAndType(androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy policy, androidx.wear.watchface.complications.data.ComplicationType type);
+ method @Deprecated public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getPolicy();
+ method @Deprecated public androidx.wear.watchface.complications.data.ComplicationType getType();
property public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy policy;
property public final androidx.wear.watchface.complications.data.ComplicationType type;
}
@@ -170,7 +169,7 @@
public interface WatchFaceControlClient extends java.lang.AutoCloseable {
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public default static suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient> p) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
- method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName) throws android.os.RemoteException;
+ method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient() throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public suspend Object? getOrCreateInteractiveWatchFaceClient(String id, androidx.wear.watchface.client.DeviceConfig deviceConfig, androidx.wear.watchface.client.WatchUiState watchUiState, androidx.wear.watchface.style.UserStyleData? userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.watchface.complications.data.ComplicationData>? slotIdToComplicationData, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.InteractiveWatchFaceClient> p) throws android.os.RemoteException;
@@ -195,15 +194,14 @@
property public final String id;
}
- @androidx.wear.watchface.client.WatchFaceClientExperimental public interface WatchFaceMetadataClient extends java.lang.AutoCloseable {
- method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public default static suspend Object? createWatchFaceMetadataClient(android.content.Context context, android.content.ComponentName watchFaceName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient> p) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
- method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationSlotMetadata> getComplicationSlotMetadataMap() throws android.os.RemoteException;
- method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.style.UserStyleSchema getUserStyleSchema() throws android.os.RemoteException;
+ public interface WatchFaceMetadataClient extends java.lang.AutoCloseable {
+ method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationSlotMetadata> getComplicationSlotMetadataMap();
+ method public androidx.wear.watchface.style.UserStyleSchema getUserStyleSchema();
field public static final androidx.wear.watchface.client.WatchFaceMetadataClient.Companion Companion;
}
public static final class WatchFaceMetadataClient.Companion {
- method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? createWatchFaceMetadataClient(android.content.Context context, android.content.ComponentName watchFaceName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient> p) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
+ method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? create(android.content.Context context, android.content.ComponentName watchFaceName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient> p) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
}
public static final class WatchFaceMetadataClient.ServiceNotBoundException extends java.lang.Exception {
diff --git a/wear/watchface/watchface-client/api/restricted_current.txt b/wear/watchface/watchface-client/api/restricted_current.txt
index 19c1c16..750f771 100644
--- a/wear/watchface/watchface-client/api/restricted_current.txt
+++ b/wear/watchface/watchface-client/api/restricted_current.txt
@@ -1,14 +1,33 @@
// Signature format: 4.0
package androidx.wear.watchface.client {
+ public final class ComplicationSlotMetadata {
+ ctor public ComplicationSlotMetadata(androidx.wear.watchface.complications.ComplicationSlotBounds? bounds, @androidx.wear.watchface.ComplicationSlotBoundsType int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, boolean isInitiallyEnabled, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
+ method public androidx.wear.watchface.complications.ComplicationSlotBounds? getBounds();
+ method public int getBoundsType();
+ method public android.os.Bundle getComplicationConfigExtras();
+ method public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getDefaultDataSourcePolicy();
+ method public boolean getFixedComplicationDataSource();
+ method public java.util.List<androidx.wear.watchface.complications.data.ComplicationType> getSupportedTypes();
+ method public boolean isInitiallyEnabled();
+ property public final androidx.wear.watchface.complications.ComplicationSlotBounds? bounds;
+ property public final int boundsType;
+ property public final android.os.Bundle complicationConfigExtras;
+ property public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy;
+ property public final boolean fixedComplicationDataSource;
+ property public final boolean isInitiallyEnabled;
+ property public final java.util.List<androidx.wear.watchface.complications.data.ComplicationType> supportedTypes;
+ }
+
public final class ComplicationSlotState {
- ctor public ComplicationSlotState(android.graphics.Rect bounds, @androidx.wear.watchface.ComplicationSlotBoundsType int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType, boolean isEnabled, boolean isInitiallyEnabled, androidx.wear.watchface.complications.data.ComplicationType currentType, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
+ ctor public ComplicationSlotState(android.graphics.Rect bounds, @androidx.wear.watchface.ComplicationSlotBoundsType int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, boolean isEnabled, boolean isInitiallyEnabled, androidx.wear.watchface.complications.data.ComplicationType currentType, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
+ ctor @Deprecated public ComplicationSlotState(android.graphics.Rect bounds, @androidx.wear.watchface.ComplicationSlotBoundsType int boundsType, java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationType> supportedTypes, androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy, androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType, boolean isEnabled, boolean isInitiallyEnabled, androidx.wear.watchface.complications.data.ComplicationType currentType, boolean fixedComplicationDataSource, android.os.Bundle complicationConfigExtras);
method public android.graphics.Rect getBounds();
method public int getBoundsType();
method public android.os.Bundle getComplicationConfigExtras();
method public androidx.wear.watchface.complications.data.ComplicationType getCurrentType();
method public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getDefaultDataSourcePolicy();
- method public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
+ method @Deprecated public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
method public boolean getFixedComplicationDataSource();
method public java.util.List<androidx.wear.watchface.complications.data.ComplicationType> getSupportedTypes();
method public boolean isEnabled();
@@ -18,17 +37,17 @@
property public final android.os.Bundle complicationConfigExtras;
property public final androidx.wear.watchface.complications.data.ComplicationType currentType;
property public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy;
- property public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
+ property @Deprecated public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
property public final boolean fixedComplicationDataSource;
property public final boolean isEnabled;
property public final boolean isInitiallyEnabled;
property public final java.util.List<androidx.wear.watchface.complications.data.ComplicationType> supportedTypes;
}
- public final class DefaultComplicationDataSourcePolicyAndType {
- ctor public DefaultComplicationDataSourcePolicyAndType(androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy policy, androidx.wear.watchface.complications.data.ComplicationType type);
- method public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getPolicy();
- method public androidx.wear.watchface.complications.data.ComplicationType getType();
+ @Deprecated public final class DefaultComplicationDataSourcePolicyAndType {
+ ctor @Deprecated public DefaultComplicationDataSourcePolicyAndType(androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy policy, androidx.wear.watchface.complications.data.ComplicationType type);
+ method @Deprecated public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getPolicy();
+ method @Deprecated public androidx.wear.watchface.complications.data.ComplicationType getType();
property public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy policy;
property public final androidx.wear.watchface.complications.data.ComplicationType type;
}
@@ -147,7 +166,7 @@
public interface WatchFaceControlClient extends java.lang.AutoCloseable {
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public default static suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient> p) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
- method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName) throws android.os.RemoteException;
+ method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient() throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public suspend Object? getOrCreateInteractiveWatchFaceClient(String id, androidx.wear.watchface.client.DeviceConfig deviceConfig, androidx.wear.watchface.client.WatchUiState watchUiState, androidx.wear.watchface.style.UserStyleData? userStyle, java.util.Map<java.lang.Integer,? extends androidx.wear.watchface.complications.data.ComplicationData>? slotIdToComplicationData, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.InteractiveWatchFaceClient> p) throws android.os.RemoteException;
@@ -172,6 +191,24 @@
property public final String id;
}
+ public interface WatchFaceMetadataClient extends java.lang.AutoCloseable {
+ method public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.ComplicationSlotMetadata> getComplicationSlotMetadataMap();
+ method public androidx.wear.watchface.style.UserStyleSchema getUserStyleSchema();
+ field public static final androidx.wear.watchface.client.WatchFaceMetadataClient.Companion Companion;
+ }
+
+ public static final class WatchFaceMetadataClient.Companion {
+ method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? create(android.content.Context context, android.content.ComponentName watchFaceName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient> p) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
+ }
+
+ public static final class WatchFaceMetadataClient.ServiceNotBoundException extends java.lang.Exception {
+ ctor public WatchFaceMetadataClient.ServiceNotBoundException();
+ }
+
+ public static final class WatchFaceMetadataClient.ServiceStartFailureException extends java.lang.Exception {
+ ctor public WatchFaceMetadataClient.ServiceStartFailureException(optional String message);
+ }
+
public final class WatchUiState {
ctor public WatchUiState(boolean inAmbientMode, int interruptionFilter);
method public boolean getInAmbientMode();
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 552c556..a9a410d 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
@@ -36,6 +36,7 @@
import androidx.test.filters.MediumTest
import androidx.test.screenshot.AndroidXScreenshotTestRule
import androidx.test.screenshot.assertAgainstGolden
+import androidx.wear.watchface.CanvasComplication
import androidx.wear.watchface.complications.ComplicationSlotBounds
import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
import androidx.wear.watchface.complications.SystemDataSources
@@ -57,12 +58,13 @@
import androidx.wear.watchface.WatchFaceService
import androidx.wear.watchface.WatchFaceType
import androidx.wear.watchface.WatchState
-import androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType
import androidx.wear.watchface.client.DeviceConfig
import androidx.wear.watchface.client.HeadlessWatchFaceClient
import androidx.wear.watchface.client.InteractiveWatchFaceClient
import androidx.wear.watchface.client.WatchFaceControlClient
import androidx.wear.watchface.client.WatchUiState
+import androidx.wear.watchface.complications.data.ComplicationData
+import androidx.wear.watchface.complications.data.NoDataComplicationData
import androidx.wear.watchface.control.WatchFaceControlService
import androidx.wear.watchface.samples.BLUE_STYLE
import androidx.wear.watchface.samples.COLOR_STYLE_SETTING
@@ -320,6 +322,7 @@
headlessInstance.close()
}
+ @Suppress("DEPRECATION") // defaultDataSourceType
@Test
fun headlessComplicationDetails() {
val headlessInstance = service.createHeadlessWatchFaceClient(
@@ -381,6 +384,52 @@
}
@Test
+ fun complicationProviderDefaults() {
+ val wallpaperService = TestComplicationProviderDefaultsWatchFaceService(
+ context,
+ surfaceHolder
+ )
+ val deferredInteractiveInstance = handlerCoroutineScope.async {
+ service.getOrCreateInteractiveWatchFaceClient(
+ "testId",
+ deviceConfig,
+ systemState,
+ null,
+ complications
+ )
+ }
+ // Create the engine which triggers the crashing watchface.
+ handler.post {
+ engine = wallpaperService.onCreateEngine() as WatchFaceService.EngineWrapper
+ }
+
+ // Wait for the instance to be created.
+ val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+
+ try {
+ assertThat(interactiveInstance.complicationSlotsState.keys).containsExactly(123)
+
+ val slot = interactiveInstance.complicationSlotsState[123]!!
+ assertThat(slot.defaultDataSourcePolicy.primaryDataSource)
+ .isEqualTo(ComponentName("com.package1", "com.app1"))
+ assertThat(slot.defaultDataSourcePolicy.primaryDataSourceDefaultType)
+ .isEqualTo(ComplicationType.PHOTO_IMAGE)
+
+ assertThat(slot.defaultDataSourcePolicy.secondaryDataSource)
+ .isEqualTo(ComponentName("com.package2", "com.app2"))
+ assertThat(slot.defaultDataSourcePolicy.secondaryDataSourceDefaultType)
+ .isEqualTo(ComplicationType.LONG_TEXT)
+
+ assertThat(slot.defaultDataSourcePolicy.systemDataSourceFallback)
+ .isEqualTo(SystemDataSources.DATA_SOURCE_STEP_COUNT)
+ assertThat(slot.defaultDataSourcePolicy.systemDataSourceFallbackDefaultType)
+ .isEqualTo(ComplicationType.SHORT_TEXT)
+ } finally {
+ interactiveInstance.close()
+ }
+ }
+
+ @Test
fun headlessUserStyleSchema() {
val headlessInstance = service.createHeadlessWatchFaceClient(
exampleWatchFaceComponentName,
@@ -499,6 +548,7 @@
}
}
+ @Suppress("DEPRECATION") // defaultDataSourceType
@Test
fun interactiveWatchFaceClient_ComplicationDetails() {
val deferredInteractiveInstance = handlerCoroutineScope.async {
@@ -1026,6 +1076,7 @@
}
}
+ @Suppress("DEPRECATION") // DefaultComplicationDataSourcePolicyAndType
@Test
fun getDefaultProviderPolicies() {
assertThat(
@@ -1033,14 +1084,14 @@
).containsExactlyEntriesIn(
mapOf(
EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID to
- DefaultComplicationDataSourcePolicyAndType(
+ androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType(
DefaultComplicationDataSourcePolicy(
SystemDataSources.DATA_SOURCE_DAY_OF_WEEK
),
ComplicationType.SHORT_TEXT
),
EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID to
- DefaultComplicationDataSourcePolicyAndType(
+ androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType(
DefaultComplicationDataSourcePolicy(
SystemDataSources.DATA_SOURCE_STEP_COUNT
),
@@ -1050,6 +1101,7 @@
)
}
+ @Suppress("DEPRECATION") // DefaultComplicationDataSourcePolicyAndType
@Test
fun getDefaultProviderPoliciesOldApi() {
WatchFaceControlTestService.apiVersionOverride = 1
@@ -1058,14 +1110,14 @@
).containsExactlyEntriesIn(
mapOf(
EXAMPLE_CANVAS_WATCHFACE_LEFT_COMPLICATION_ID to
- DefaultComplicationDataSourcePolicyAndType(
+ androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType(
DefaultComplicationDataSourcePolicy(
SystemDataSources.DATA_SOURCE_DAY_OF_WEEK
),
ComplicationType.SHORT_TEXT
),
EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID to
- DefaultComplicationDataSourcePolicyAndType(
+ androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType(
DefaultComplicationDataSourcePolicy(
SystemDataSources.DATA_SOURCE_STEP_COUNT
),
@@ -1075,6 +1127,7 @@
)
}
+ @Suppress("DEPRECATION") // DefaultComplicationDataSourcePolicyAndType
@Test
fun getDefaultProviderPolicies_with_TestCrashingWatchFaceService() {
// Tests that we can retrieve the DefaultComplicationDataSourcePolicy without invoking any
@@ -1090,7 +1143,7 @@
).containsExactlyEntriesIn(
mapOf(
TestCrashingWatchFaceService.COMPLICATION_ID to
- DefaultComplicationDataSourcePolicyAndType(
+ androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType(
DefaultComplicationDataSourcePolicy(
SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET
),
@@ -1321,10 +1374,11 @@
{ _, _ -> throw Exception("Deliberately crashing") },
listOf(ComplicationType.LONG_TEXT),
DefaultComplicationDataSourcePolicy(
- SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET
+ SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET,
+ ComplicationType.LONG_TEXT
),
ComplicationSlotBounds(RectF(0.1f, 0.1f, 0.4f, 0.4f))
- ).setDefaultDataSourceType(ComplicationType.LONG_TEXT).build()
+ ).build()
),
currentUserStyleRepository
)
@@ -1444,3 +1498,96 @@
}
)
}
+
+internal class TestComplicationProviderDefaultsWatchFaceService(
+ testContext: Context,
+ private var surfaceHolderOverride: SurfaceHolder
+) : WatchFaceService() {
+ internal lateinit var watchFace: WatchFace
+
+ init {
+ attachBaseContext(testContext)
+ }
+
+ override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+ override fun createComplicationSlotsManager(
+ currentUserStyleRepository: CurrentUserStyleRepository
+ ): ComplicationSlotsManager {
+ return ComplicationSlotsManager(
+ listOf(
+ ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ 123,
+ { _, _ ->
+ object : CanvasComplication {
+ override fun render(
+ canvas: Canvas,
+ bounds: Rect,
+ zonedDateTime: ZonedDateTime,
+ renderParameters: RenderParameters,
+ slotId: Int
+ ) {}
+
+ override fun drawHighlight(
+ canvas: Canvas,
+ bounds: Rect,
+ boundsType: Int,
+ zonedDateTime: ZonedDateTime,
+ color: Int
+ ) {}
+
+ override fun getData() = NoDataComplicationData()
+
+ override fun loadData(
+ complicationData: ComplicationData,
+ loadDrawablesAsynchronous: Boolean
+ ) {}
+ }
+ },
+ listOf(
+ ComplicationType.PHOTO_IMAGE,
+ ComplicationType.LONG_TEXT,
+ ComplicationType.SHORT_TEXT
+ ),
+ DefaultComplicationDataSourcePolicy(
+ ComponentName("com.package1", "com.app1"),
+ ComplicationType.PHOTO_IMAGE,
+ ComponentName("com.package2", "com.app2"),
+ ComplicationType.LONG_TEXT,
+ SystemDataSources.DATA_SOURCE_STEP_COUNT,
+ ComplicationType.SHORT_TEXT
+ ),
+ ComplicationSlotBounds(
+ RectF(0.1f, 0.2f, 0.3f, 0.4f)
+ )
+ )
+ .build()
+ ),
+ currentUserStyleRepository
+ )
+ }
+
+ override suspend fun createWatchFace(
+ surfaceHolder: SurfaceHolder,
+ watchState: WatchState,
+ complicationSlotsManager: ComplicationSlotsManager,
+ currentUserStyleRepository: CurrentUserStyleRepository
+ ) = WatchFace(
+ WatchFaceType.DIGITAL,
+ object : Renderer.CanvasRenderer(
+ surfaceHolder,
+ currentUserStyleRepository,
+ watchState,
+ CanvasType.HARDWARE,
+ 16
+ ) {
+ override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {}
+
+ override fun renderHighlightLayer(
+ canvas: Canvas,
+ bounds: Rect,
+ zonedDateTime: ZonedDateTime
+ ) {}
+ }
+ )
+}
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceMetadataServiceTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceMetadataServiceTest.kt
index 9652dd3..1db16ba 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceMetadataServiceTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceMetadataServiceTest.kt
@@ -26,7 +26,6 @@
import androidx.test.filters.MediumTest
import androidx.wear.watchface.complications.SystemDataSources
import androidx.wear.watchface.complications.data.ComplicationType
-import androidx.wear.watchface.client.WatchFaceClientExperimental
import androidx.wear.watchface.client.WatchFaceMetadataClient
import androidx.wear.watchface.control.WatchFaceControlService
import androidx.wear.watchface.ComplicationSlotBoundsType
@@ -40,7 +39,6 @@
@RunWith(AndroidJUnit4::class)
@MediumTest
-@OptIn(WatchFaceClientExperimental::class)
public class WatchFaceMetadataServiceTest {
private val exampleWatchFaceComponentName = ComponentName(
"androidx.wear.watchface.client.test",
@@ -49,7 +47,7 @@
private val context = ApplicationProvider.getApplicationContext<Context>()
private val service = runBlocking {
- WatchFaceMetadataClient.createWatchFaceMetadataClientImpl(
+ WatchFaceMetadataClient.createImpl(
context,
Intent(context, WatchFaceControlTestService::class.java).apply {
action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
@@ -124,9 +122,10 @@
Truth.assertThat(
leftComplicationMetadata.defaultDataSourcePolicy.systemDataSourceFallback
).isEqualTo(SystemDataSources.DATA_SOURCE_DAY_OF_WEEK)
- Truth.assertThat(leftComplicationMetadata.defaultDataSourceType).isEqualTo(
- ComplicationType.SHORT_TEXT
- )
+ Truth.assertThat(
+ leftComplicationMetadata.defaultDataSourcePolicy
+ .systemDataSourceFallbackDefaultType
+ ).isEqualTo(ComplicationType.SHORT_TEXT)
Truth.assertThat(leftComplicationMetadata.supportedTypes).containsExactly(
ComplicationType.RANGED_VALUE,
ComplicationType.LONG_TEXT,
@@ -148,9 +147,10 @@
Truth.assertThat(
rightComplicationMetadata.defaultDataSourcePolicy.systemDataSourceFallback
).isEqualTo(SystemDataSources.DATA_SOURCE_STEP_COUNT)
- Truth.assertThat(rightComplicationMetadata.defaultDataSourceType).isEqualTo(
- ComplicationType.SHORT_TEXT
- )
+ Truth.assertThat(
+ rightComplicationMetadata.defaultDataSourcePolicy
+ .systemDataSourceFallbackDefaultType
+ ).isEqualTo(ComplicationType.SHORT_TEXT)
Truth.assertThat(rightComplicationMetadata.supportedTypes).containsExactly(
ComplicationType.RANGED_VALUE,
ComplicationType.LONG_TEXT,
@@ -163,7 +163,7 @@
@Test
public fun getSchema_static_metadata() {
runBlocking {
- val client = WatchFaceMetadataClient.createWatchFaceMetadataClientImpl(
+ val client = WatchFaceMetadataClient.createImpl(
context,
Intent(context, WatchFaceControlTestService::class.java).apply {
action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
@@ -183,4 +183,90 @@
)
}
}
+
+ @Test
+ public fun getComplicationSlotMetadataMap_static_metadata() {
+ runBlocking {
+ val client = WatchFaceMetadataClient.createImpl(
+ context,
+ Intent(context, WatchFaceControlTestService::class.java).apply {
+ action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
+ },
+ exampleWatchFaceComponentName,
+ object : WatchFaceMetadataClient.Companion.ParserProvider() {
+ override fun getParser(
+ context: Context,
+ watchFaceName: ComponentName
+ ) = context.resources.getXml(R.xml.xml_watchface)
+ }
+ )
+ val complications = client.getComplicationSlotMetadataMap()
+
+ Truth.assertThat(complications.keys).containsExactly(10, 20)
+
+ Truth.assertThat(complications[10]!!.boundsType)
+ .isEqualTo(ComplicationSlotBoundsType.ROUND_RECT)
+
+ Truth.assertThat(complications[10]!!.supportedTypes)
+ .containsExactly(
+ ComplicationType.SHORT_TEXT,
+ ComplicationType.RANGED_VALUE,
+ ComplicationType.SMALL_IMAGE
+ )
+
+ Truth.assertThat(complications[10]!!.defaultDataSourcePolicy.primaryDataSource)
+ .isEqualTo(ComponentName("com.app.example1", "com.app.example1.Class"))
+
+ Truth.assertThat(
+ complications[10]!!.defaultDataSourcePolicy.primaryDataSourceDefaultType
+ ).isEqualTo(ComplicationType.SHORT_TEXT)
+
+ Truth.assertThat(complications[10]!!.defaultDataSourcePolicy.secondaryDataSource)
+ .isEqualTo(ComponentName("com.app.example2", "com.app.example2.Class"))
+
+ Truth.assertThat(
+ complications[10]!!.defaultDataSourcePolicy.secondaryDataSourceDefaultType
+ ).isEqualTo(ComplicationType.SMALL_IMAGE)
+
+ Truth.assertThat(complications[10]!!.defaultDataSourcePolicy.systemDataSourceFallback)
+ .isEqualTo(SystemDataSources.DATA_SOURCE_WATCH_BATTERY)
+
+ Truth.assertThat(
+ complications[10]!!.defaultDataSourcePolicy
+ .systemDataSourceFallbackDefaultType
+ ).isEqualTo(ComplicationType.RANGED_VALUE)
+
+ Truth.assertThat(
+ complications[10]!!.bounds!!.perComplicationTypeBounds[ComplicationType.SHORT_TEXT]
+ ).isEqualTo(RectF(0.3f, 0.7f, 0.7f, 0.9f))
+
+ Truth.assertThat(complications[20]!!.boundsType)
+ .isEqualTo(ComplicationSlotBoundsType.BACKGROUND)
+
+ Truth.assertThat(complications[20]!!.supportedTypes)
+ .containsExactly(ComplicationType.PHOTO_IMAGE)
+
+ Truth.assertThat(complications[20]!!.defaultDataSourcePolicy.primaryDataSource)
+ .isEqualTo(ComponentName("com.package", "com.app"))
+
+ Truth.assertThat(
+ complications[20]!!.defaultDataSourcePolicy.primaryDataSourceDefaultType
+ ).isEqualTo(ComplicationType.PHOTO_IMAGE)
+
+ Truth.assertThat(complications[20]!!.defaultDataSourcePolicy.secondaryDataSource)
+ .isNull()
+
+ Truth.assertThat(
+ complications[20]!!.defaultDataSourcePolicy.secondaryDataSourceDefaultType
+ ).isNull()
+
+ Truth.assertThat(complications[20]!!.defaultDataSourcePolicy.systemDataSourceFallback)
+ .isEqualTo(SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET)
+
+ Truth.assertThat(
+ complications[20]!!.defaultDataSourcePolicy
+ .systemDataSourceFallbackDefaultType
+ ).isEqualTo(ComplicationType.PHOTO_IMAGE)
+ }
+ }
}
diff --git a/wear/watchface/watchface-client/src/androidTest/res/xml/xml_watchface.xml b/wear/watchface/watchface-client/src/androidTest/res/xml/xml_watchface.xml
index a8e23f3..794d4bc 100644
--- a/wear/watchface/watchface-client/src/androidTest/res/xml/xml_watchface.xml
+++ b/wear/watchface/watchface-client/src/androidTest/res/xml/xml_watchface.xml
@@ -34,17 +34,22 @@
app:slotId="10"
app:boundsType="ROUND_RECT"
app:supportedTypes="SHORT_TEXT|RANGED_VALUE|SMALL_IMAGE"
- app:defaultDataSourceType="RANGED_VALUE"
- app:systemDataSourceFallback="DATA_SOURCE_WATCH_BATTERY">
+ app:primaryDataSource="com.app.example1/com.app.example1.Class"
+ app:primaryDataSourceDefaultType="SHORT_TEXT"
+ app:secondaryDataSource="com.app.example2/com.app.example2.Class"
+ app:secondaryDataSourceDefaultType="SMALL_IMAGE"
+ app:systemDataSourceFallback="DATA_SOURCE_WATCH_BATTERY"
+ app:systemDataSourceFallbackDefaultType="RANGED_VALUE">
<ComplicationSlotBounds app:left="0.3" app:top="0.7" app:right="0.7" app:bottom="0.9"/>
</ComplicationSlot>
<ComplicationSlot
app:slotId="20"
app:boundsType="BACKGROUND"
- app:supportedTypes="LONG_TEXT"
- app:defaultDataSourceType="LONG_TEXT"
+ app:supportedTypes="PHOTO_IMAGE"
app:primaryDataSource="com.package/com.app"
- app:systemDataSourceFallback="DATA_SOURCE_SUNRISE_SUNSET">
+ app:primaryDataSourceDefaultType="PHOTO_IMAGE"
+ app:systemDataSourceFallback="DATA_SOURCE_SUNRISE_SUNSET"
+ app:systemDataSourceFallbackDefaultType="PHOTO_IMAGE">
<ComplicationSlotBounds app:left="0.1" app:top="0.2" app:right="0.3" app:bottom="0.4"/>
</ComplicationSlot>
-</XmlWatchFace>
\ No newline at end of file
+</XmlWatchFace>
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/ComplicationSlotState.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/ComplicationSlotState.kt
index 135bb6a..05d92d5 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/ComplicationSlotState.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/ComplicationSlotState.kt
@@ -28,39 +28,163 @@
import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption
import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay
-/**
- * State of the [ComplicationSlot].
- *
- * @param bounds Screen space bounds of the [ComplicationSlot] in pixels.
- * @param boundsType The type of the complication's bounds.
- * @param supportedTypes The [ComplicationType]s supported by this complication.
- * @param defaultDataSourcePolicy The [DefaultComplicationDataSourcePolicy] for this complication.
- * @param defaultDataSourceType The default [ComplicationType] for this complication.
- * @param isEnabled Whether or not the complication is currently enabled (i.e. it should be drawn).
- * @param isInitiallyEnabled Whether or not the complication was initially enabled before
- * considering any [ComplicationSlotsOption] whose [ComplicationSlotOverlay]s may enable or disable
- * complicationSlots.
- * @param currentType The [ComplicationType] of the complication's current [ComplicationData].
- * @param fixedComplicationDataSource Whether or not the complication data source is fixed (i.e the
- * user can't configure it).
- * @param complicationConfigExtras Extras to be merged into the Intent sent when invoking the
- * complication data source chooser activity.
- */
-public class ComplicationSlotState(
- public val bounds: Rect,
- @ComplicationSlotBoundsType public val boundsType: Int,
- public val supportedTypes: List<ComplicationType>,
- public val defaultDataSourcePolicy: DefaultComplicationDataSourcePolicy,
- public val defaultDataSourceType: ComplicationType,
+/** A snapshot of the state of a watch face [ComplicationSlot]. */
+public class ComplicationSlotState {
+ /** Screen space bounds of the [ComplicationSlot] in pixels. */
+ public val bounds: Rect
+
+ /** The type of the complication's bounds. */
+ @ComplicationSlotBoundsType
+ public val boundsType: Int
+
+ /** The [ComplicationType]s supported by this complication. */
+ public val supportedTypes: List<ComplicationType>
+
+ /** The [DefaultComplicationDataSourcePolicy] for this complication slot. */
+ public val defaultDataSourcePolicy: DefaultComplicationDataSourcePolicy
+
+ /** The default [ComplicationType] for this complication. */
+ @Deprecated("Use defaultDataSourcePolicy.systemDataSourceFallbackDefaultType instead")
+ public val defaultDataSourceType: ComplicationType
+ get() = defaultDataSourcePolicy.systemDataSourceFallbackDefaultType
+
+ /** Whether or not the complication is currently enabled (i.e. it should be drawn. */
@get:JvmName("isEnabled")
- public val isEnabled: Boolean,
+ public val isEnabled: Boolean
+
+ /**
+ * Whether or not the complication was initially enabled before considering any
+ * [ComplicationSlotsOption] whose [ComplicationSlotOverlay]s may enable or disable
+ * complicationSlots.
+ */
@get:JvmName("isInitiallyEnabled")
- public val isInitiallyEnabled: Boolean,
- public val currentType: ComplicationType,
- @get:JvmName("isFixedComplicationDataSource")
- public val fixedComplicationDataSource: Boolean,
+ public val isInitiallyEnabled: Boolean
+
+ /** The [ComplicationType] of the complication's current [ComplicationData]. */
+ public val currentType: ComplicationType
+
+ /** Whether or not the complication data source is fixed (i.e the user can't configure it). */
+ public val fixedComplicationDataSource: Boolean
+
+ /**
+ * Extras to be merged into the Intent sent when invoking the complication data source chooser
+ * activity.
+ */
public val complicationConfigExtras: Bundle
-) {
+
+ /**
+ * @param bounds Screen space bounds of the [ComplicationSlot] in pixels.
+ * @param boundsType The type of the complication's bounds.
+ * @param supportedTypes The [ComplicationType]s supported by this complication.
+ * @param defaultDataSourcePolicy The [DefaultComplicationDataSourcePolicy] for this
+ * complication slot.
+ * @param isEnabled Whether or not the complication is currently enabled (i.e. it should be
+ * drawn).
+ * @param isInitiallyEnabled Whether or not the complication was initially enabled before
+ * considering any [ComplicationSlotsOption] whose [ComplicationSlotOverlay]s may enable or
+ * disable complicationSlots.
+ * @param currentType The [ComplicationType] of the complication's current [ComplicationData].
+ * @param fixedComplicationDataSource Whether or not the complication data source is fixed (i.e
+ * the user can't configure it).
+ * @param complicationConfigExtras Extras to be merged into the Intent sent when invoking the
+ * complication data source chooser activity.
+ */
+ public constructor(
+ bounds: Rect,
+ @ComplicationSlotBoundsType boundsType: Int,
+ supportedTypes: List<ComplicationType>,
+ defaultDataSourcePolicy: DefaultComplicationDataSourcePolicy,
+ isEnabled: Boolean,
+ isInitiallyEnabled: Boolean,
+ currentType: ComplicationType,
+ fixedComplicationDataSource: Boolean,
+ complicationConfigExtras: Bundle
+ ) {
+ this.bounds = bounds
+ this.boundsType = boundsType
+ this.supportedTypes = supportedTypes
+ this.defaultDataSourcePolicy = defaultDataSourcePolicy
+ this.isEnabled = isEnabled
+ this.isInitiallyEnabled = isInitiallyEnabled
+ this.currentType = currentType
+ this.fixedComplicationDataSource = fixedComplicationDataSource
+ this.complicationConfigExtras = complicationConfigExtras
+ }
+
+ /**
+ * @param bounds Screen space bounds of the [ComplicationSlot] in pixels.
+ * @param boundsType The type of the complication's bounds.
+ * @param supportedTypes The [ComplicationType]s supported by this complication.
+ * @param defaultDataSourcePolicy The [DefaultComplicationDataSourcePolicy] for this
+ * complication slot.
+ * @param defaultDataSourceType The default [ComplicationType] for this complication.
+ * @param isEnabled Whether or not the complication is currently enabled (i.e. it should be
+ * drawn).
+ * @param isInitiallyEnabled Whether or not the complication was initially enabled before
+ * considering any [ComplicationSlotsOption] whose [ComplicationSlotOverlay]s may enable or
+ * disable complicationSlots.
+ * @param currentType The [ComplicationType] of the complication's current [ComplicationData].
+ * @param fixedComplicationDataSource Whether or not the complication data source is fixed (i.e
+ * the user can't configure it).
+ * @param complicationConfigExtras Extras to be merged into the Intent sent when invoking the
+ * complication data source chooser activity.
+ */
+ @Deprecated(
+ "defaultDataSourceType is depreciated", ReplaceWith(
+ "ComplicationSlotState(Rect, Int, List<ComplicationType>, " +
+ "DefaultComplicationDataSourcePolicy, Boolean, Boolean, ComplicationType, Boolean" +
+ ", Bundle)"
+ )
+ )
+ public constructor(
+ bounds: Rect,
+ @ComplicationSlotBoundsType boundsType: Int,
+ supportedTypes: List<ComplicationType>,
+ defaultDataSourcePolicy: DefaultComplicationDataSourcePolicy,
+ defaultDataSourceType: ComplicationType,
+ isEnabled: Boolean,
+ isInitiallyEnabled: Boolean,
+ currentType: ComplicationType,
+ fixedComplicationDataSource: Boolean,
+ complicationConfigExtras: Bundle
+ ) {
+ this.bounds = bounds
+ this.boundsType = boundsType
+ this.supportedTypes = supportedTypes
+ this.defaultDataSourcePolicy = when {
+ defaultDataSourcePolicy.secondaryDataSource != null ->
+ DefaultComplicationDataSourcePolicy(
+ defaultDataSourcePolicy.primaryDataSource!!,
+ defaultDataSourcePolicy.primaryDataSourceDefaultType
+ ?: defaultDataSourceType,
+ defaultDataSourcePolicy.secondaryDataSource!!,
+ defaultDataSourcePolicy.secondaryDataSourceDefaultType
+ ?: defaultDataSourceType,
+ defaultDataSourcePolicy.systemDataSourceFallback,
+ defaultDataSourceType
+ )
+
+ defaultDataSourcePolicy.primaryDataSource != null ->
+ DefaultComplicationDataSourcePolicy(
+ defaultDataSourcePolicy.primaryDataSource!!,
+ defaultDataSourcePolicy.primaryDataSourceDefaultType
+ ?: defaultDataSourceType,
+ defaultDataSourcePolicy.systemDataSourceFallback,
+ defaultDataSourceType
+ )
+
+ else -> DefaultComplicationDataSourcePolicy(
+ defaultDataSourcePolicy.systemDataSourceFallback,
+ defaultDataSourceType
+ )
+ }
+ this.isEnabled = isEnabled
+ this.isInitiallyEnabled = isInitiallyEnabled
+ this.currentType = currentType
+ this.fixedComplicationDataSource = fixedComplicationDataSource
+ this.complicationConfigExtras = complicationConfigExtras
+ }
+
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public constructor(
@@ -70,10 +194,16 @@
complicationStateWireFormat.boundsType,
complicationStateWireFormat.supportedTypes.map { ComplicationType.fromWireType(it) },
DefaultComplicationDataSourcePolicy(
- complicationStateWireFormat.defaultProvidersToTry ?: emptyList(),
- complicationStateWireFormat.fallbackSystemProvider
+ complicationStateWireFormat.defaultDataSourcesToTry ?: emptyList(),
+ complicationStateWireFormat.fallbackSystemProvider,
+ ComplicationType.fromWireType(
+ complicationStateWireFormat.primaryDataSourceDefaultType
+ ),
+ ComplicationType.fromWireType(
+ complicationStateWireFormat.secondaryDataSourceDefaultType
+ ),
+ ComplicationType.fromWireType(complicationStateWireFormat.defaultDataSourceType)
),
- ComplicationType.fromWireType(complicationStateWireFormat.defaultProviderType),
complicationStateWireFormat.isEnabled,
complicationStateWireFormat.isInitiallyEnabled,
ComplicationType.fromWireType(complicationStateWireFormat.currentType),
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
index 7bd8ca3..a0cf0ee 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
@@ -196,6 +196,8 @@
* [DefaultComplicationDataSourcePolicyAndType]s for. It must be in the same APK the
* WatchFaceControlClient is connected to. NB a single apk can contain multiple watch faces.
*/
+ @Deprecated("Use the WatchFaceMetadataClient instead.")
+ @Suppress("DEPRECATION") // DefaultComplicationDataSourcePolicyAndType
@Throws(RemoteException::class)
public fun getDefaultComplicationDataSourcePoliciesAndType(
watchFaceName: ComponentName
@@ -210,10 +212,12 @@
* [androidx.wear.watchface.ComplicationSlot].
* @param type The default [ComplicationType] for the [androidx.wear.watchface.ComplicationSlot].
*/
+@Deprecated("Use the WatchFaceMetadataClient instead.")
public class DefaultComplicationDataSourcePolicyAndType(
public val policy: DefaultComplicationDataSourcePolicy,
public val type: ComplicationType
) {
+ @Suppress("DEPRECATION") // DefaultComplicationDataSourcePolicyAndType
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
@@ -351,6 +355,7 @@
return EditorServiceClientImpl(service.editorService)
}
+ @Suppress("DEPRECATION") // DefaultComplicationDataSourcePolicyAndType
override fun getDefaultComplicationDataSourcePoliciesAndType(
watchFaceName: ComponentName
): Map<Int, DefaultComplicationDataSourcePolicyAndType> = TraceEvent(
@@ -368,7 +373,10 @@
DefaultComplicationDataSourcePolicyAndType(
DefaultComplicationDataSourcePolicy(
it.defaultProvidersToTry ?: emptyList(),
- it.fallbackSystemProvider
+ it.fallbackSystemProvider,
+ ComplicationType.fromWireType(it.defaultProviderType),
+ ComplicationType.fromWireType(it.defaultProviderType),
+ ComplicationType.fromWireType(it.defaultProviderType)
),
ComplicationType.fromWireType(it.defaultProviderType)
)
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
index 17b7ab2..a52081c 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
@@ -48,9 +48,8 @@
/**
* Interface for fetching watch face metadata. E.g. the [UserStyleSchema] and
- * [ComplicationSlotMetadata].
+ * [ComplicationSlotMetadata]. This must be [close]d after use to release resources.
*/
-@WatchFaceClientExperimental
public interface WatchFaceMetadataClient : AutoCloseable {
public companion object {
@@ -67,18 +66,18 @@
* androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data node then
* [PackageManager.NameNotFoundException] is thrown if [watchFaceName] is invalid.
*/
- @JvmStatic
@Throws(
ServiceNotBoundException::class,
ServiceStartFailureException::class,
PackageManager.NameNotFoundException::class
)
- public suspend fun createWatchFaceMetadataClient(
+ @SuppressWarnings("MissingJvmstatic") // Can't really call a suspend fun from java.
+ public suspend fun create(
context: Context,
watchFaceName: ComponentName
): WatchFaceMetadataClient {
// Fallback to binding the service (slow).
- return createWatchFaceMetadataClientImpl(
+ return createImpl(
context,
Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
setPackage(watchFaceName.packageName)
@@ -107,7 +106,7 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Suppress("ShowingMemberInHiddenClass") // Spurious warning about exposing the
// 'hidden' companion object, which _isn't_ hidden.
- public suspend fun createWatchFaceMetadataClientImpl(
+ public suspend fun createImpl(
context: Context,
intent: Intent,
watchFaceName: ComponentName,
@@ -164,14 +163,12 @@
/**
* Returns the watch face's [UserStyleSchema].
*/
- @Throws(RemoteException::class)
public fun getUserStyleSchema(): UserStyleSchema
/**
* Returns a map of [androidx.wear.watchface.ComplicationSlot] ID to [ComplicationSlotMetadata]
* for each slot in the watch face's [androidx.wear.watchface.ComplicationSlotsManager].
*/
- @Throws(RemoteException::class)
public fun getComplicationSlotMetadataMap(): Map<Int, ComplicationSlotMetadata>
}
@@ -185,8 +182,6 @@
* during complication data source selection, this list should be non-empty.
* @param defaultDataSourcePolicy The [DefaultComplicationDataSourcePolicy] which controls the
* initial complication data source when the watch face is first installed.
- * @param defaultDataSourceType The default [ComplicationType] for the default complication data
- * source.
* @param isInitiallyEnabled At creation a complication slot is either enabled or disabled. This
* can be overridden by a [ComplicationSlotsUserStyleSetting] (see
* [ComplicationSlotOverlay.enabled]).
@@ -196,22 +191,19 @@
* source is fixed (i.e. can't be changed by the user). This is useful for watch faces built
* around specific complication complication data sources.
* @param complicationConfigExtras Extras to be merged into the Intent sent when invoking the
- * complication data source chooser activity.
+ * complication data source chooser activity.
*/
-@WatchFaceClientExperimental
public class ComplicationSlotMetadata(
public val bounds: ComplicationSlotBounds?,
@ComplicationSlotBoundsType public val boundsType: Int,
public val supportedTypes: List<ComplicationType>,
public val defaultDataSourcePolicy: DefaultComplicationDataSourcePolicy,
- public val defaultDataSourceType: ComplicationType,
@get:JvmName("isInitiallyEnabled")
public val isInitiallyEnabled: Boolean,
public val fixedComplicationDataSource: Boolean,
public val complicationConfigExtras: Bundle
)
-@OptIn(WatchFaceClientExperimental::class)
internal class WatchFaceMetadataClientImpl internal constructor(
private val context: Context,
private val service: IWatchFaceControlService,
@@ -254,55 +246,69 @@
override fun getUserStyleSchema(): UserStyleSchema {
requireNotClosed()
- return if (service.apiVersion >= 3) {
- UserStyleSchema(service.getUserStyleSchema(GetUserStyleSchemaParams(watchFaceName)))
- } else {
- headlessClient.userStyleSchema
+ try {
+ return if (service.apiVersion >= 3) {
+ UserStyleSchema(service.getUserStyleSchema(GetUserStyleSchemaParams(watchFaceName)))
+ } else {
+ headlessClient.userStyleSchema
+ }
+ } catch (e: RemoteException) {
+ throw RuntimeException(e)
}
}
override fun getComplicationSlotMetadataMap(): Map<Int, ComplicationSlotMetadata> {
requireNotClosed()
- return if (service.apiVersion >= 3) {
- val wireFormat = service.getComplicationSlotMetadata(
- GetComplicationSlotMetadataParams(watchFaceName)
- )
- wireFormat.associateBy(
- { it.id },
- {
- val perSlotBounds = HashMap<ComplicationType, RectF>()
- for (i in it.complicationBoundsType.indices) {
- perSlotBounds[ComplicationType.fromWireType(it.complicationBoundsType[i])] =
- it.complicationBounds[i]
+ try {
+ return if (service.apiVersion >= 3) {
+ val wireFormat = service.getComplicationSlotMetadata(
+ GetComplicationSlotMetadataParams(watchFaceName)
+ )
+ wireFormat.associateBy(
+ { it.id },
+ {
+ val perSlotBounds = HashMap<ComplicationType, RectF>()
+ for (i in it.complicationBoundsType.indices) {
+ perSlotBounds[
+ ComplicationType.fromWireType(it.complicationBoundsType[i])
+ ] = it.complicationBounds[i]
+ }
+ ComplicationSlotMetadata(
+ ComplicationSlotBounds(perSlotBounds),
+ it.boundsType,
+ it.supportedTypes.map { ComplicationType.fromWireType(it) },
+ DefaultComplicationDataSourcePolicy(
+ it.defaultDataSourcesToTry ?: emptyList(),
+ it.fallbackSystemDataSource,
+ ComplicationType.fromWireType(
+ it.primaryDataSourceDefaultType
+ ),
+ ComplicationType.fromWireType(
+ it.secondaryDataSourceDefaultType
+ ),
+ ComplicationType.fromWireType(it.defaultDataSourceType)
+ ),
+ it.isInitiallyEnabled,
+ it.isFixedComplicationDataSource,
+ it.complicationConfigExtras
+ )
}
+ )
+ } else {
+ headlessClient.complicationSlotsState.mapValues {
ComplicationSlotMetadata(
- ComplicationSlotBounds(perSlotBounds),
- it.boundsType,
- it.supportedTypes.map { ComplicationType.fromWireType(it) },
- DefaultComplicationDataSourcePolicy(
- it.defaultDataSourcesToTry ?: emptyList(),
- it.fallbackSystemDataSource
- ),
- ComplicationType.fromWireType(it.defaultDataSourceType),
- it.isInitiallyEnabled,
- it.isFixedComplicationDataSource,
- it.complicationConfigExtras
+ null,
+ it.value.boundsType,
+ it.value.supportedTypes,
+ it.value.defaultDataSourcePolicy,
+ it.value.isInitiallyEnabled,
+ it.value.fixedComplicationDataSource,
+ it.value.complicationConfigExtras
)
}
- )
- } else {
- headlessClient.complicationSlotsState.mapValues {
- ComplicationSlotMetadata(
- null,
- it.value.boundsType,
- it.value.supportedTypes,
- it.value.defaultDataSourcePolicy,
- it.value.defaultDataSourceType,
- it.value.isInitiallyEnabled,
- it.value.fixedComplicationDataSource,
- it.value.complicationConfigExtras
- )
}
+ } catch (e: RemoteException) {
+ throw RuntimeException(e)
}
}
@@ -315,7 +321,6 @@
}
}
-@OptIn(WatchFaceClientExperimental::class)
internal class XmlWatchFaceMetadataClientImpl(
private val xmlSchemaAndComplicationSlotsDefinition: XmlSchemaAndComplicationSlotsDefinition
) : WatchFaceMetadataClient {
@@ -331,7 +336,6 @@
it.boundsType,
it.supportedTypes,
it.defaultDataSourcePolicy,
- it.defaultDataSourceType,
it.initiallyEnabled,
it.fixedComplicationDataSource,
Bundle()
diff --git a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
index f2755f6..e26133a 100644
--- a/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
+++ b/wear/watchface/watchface-complications-data-source/src/main/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceService.kt
@@ -205,7 +205,8 @@
* A request for representative preview data for the complication, for use in the editor UI.
* Preview data is assumed to be static per type. E.g. for a complication that displays the date
* and time of an event, rather than returning the real time it should return a fixed date and
- * time such as 10:10 Aug 1st.
+ * time such as 10:10 Aug 1st. This data may be cached by the system and the result should be
+ * constant based on the current locale.
*
* This will be called on a background thread.
*
diff --git a/wear/watchface/watchface-complications/api/current.txt b/wear/watchface/watchface-complications/api/current.txt
index 60a49fa..af0ccd0 100644
--- a/wear/watchface/watchface-complications/api/current.txt
+++ b/wear/watchface/watchface-complications/api/current.txt
@@ -47,16 +47,25 @@
public final class DefaultComplicationDataSourcePolicy {
ctor public DefaultComplicationDataSourcePolicy();
- ctor public DefaultComplicationDataSourcePolicy(int systemProvider);
- ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName dataSource, int systemDataSourceFallback);
- ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, android.content.ComponentName secondaryDataSource, int systemDataSourceFallback);
+ ctor @Deprecated public DefaultComplicationDataSourcePolicy(int systemProvider);
+ ctor public DefaultComplicationDataSourcePolicy(int systemDataSource, androidx.wear.watchface.complications.data.ComplicationType systemDataSourceDefaultType);
+ ctor @Deprecated public DefaultComplicationDataSourcePolicy(android.content.ComponentName dataSource, int systemDataSourceFallback);
+ ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, androidx.wear.watchface.complications.data.ComplicationType primaryDataSourceDefaultType, int systemDataSourceFallback, androidx.wear.watchface.complications.data.ComplicationType systemDataSourceFallbackDefaultType);
+ ctor @Deprecated public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, android.content.ComponentName secondaryDataSource, int systemDataSourceFallback);
+ ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, androidx.wear.watchface.complications.data.ComplicationType primaryDataSourceDefaultType, android.content.ComponentName secondaryDataSource, androidx.wear.watchface.complications.data.ComplicationType secondaryDataSourceDefaultType, int systemDataSourceFallback, androidx.wear.watchface.complications.data.ComplicationType systemDataSourceFallbackDefaultType);
method public android.content.ComponentName? getPrimaryDataSource();
+ method public androidx.wear.watchface.complications.data.ComplicationType? getPrimaryDataSourceDefaultType();
method public android.content.ComponentName? getSecondaryDataSource();
+ method public androidx.wear.watchface.complications.data.ComplicationType? getSecondaryDataSourceDefaultType();
method public int getSystemDataSourceFallback();
+ method public androidx.wear.watchface.complications.data.ComplicationType getSystemDataSourceFallbackDefaultType();
method public boolean isEmpty();
property public final android.content.ComponentName? primaryDataSource;
+ property public final androidx.wear.watchface.complications.data.ComplicationType? primaryDataSourceDefaultType;
property public final android.content.ComponentName? secondaryDataSource;
+ property public final androidx.wear.watchface.complications.data.ComplicationType? secondaryDataSourceDefaultType;
property public final int systemDataSourceFallback;
+ property public final androidx.wear.watchface.complications.data.ComplicationType systemDataSourceFallbackDefaultType;
}
public final class SystemDataSources {
diff --git a/wear/watchface/watchface-complications/api/public_plus_experimental_current.txt b/wear/watchface/watchface-complications/api/public_plus_experimental_current.txt
index 60a49fa..af0ccd0 100644
--- a/wear/watchface/watchface-complications/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-complications/api/public_plus_experimental_current.txt
@@ -47,16 +47,25 @@
public final class DefaultComplicationDataSourcePolicy {
ctor public DefaultComplicationDataSourcePolicy();
- ctor public DefaultComplicationDataSourcePolicy(int systemProvider);
- ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName dataSource, int systemDataSourceFallback);
- ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, android.content.ComponentName secondaryDataSource, int systemDataSourceFallback);
+ ctor @Deprecated public DefaultComplicationDataSourcePolicy(int systemProvider);
+ ctor public DefaultComplicationDataSourcePolicy(int systemDataSource, androidx.wear.watchface.complications.data.ComplicationType systemDataSourceDefaultType);
+ ctor @Deprecated public DefaultComplicationDataSourcePolicy(android.content.ComponentName dataSource, int systemDataSourceFallback);
+ ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, androidx.wear.watchface.complications.data.ComplicationType primaryDataSourceDefaultType, int systemDataSourceFallback, androidx.wear.watchface.complications.data.ComplicationType systemDataSourceFallbackDefaultType);
+ ctor @Deprecated public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, android.content.ComponentName secondaryDataSource, int systemDataSourceFallback);
+ ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, androidx.wear.watchface.complications.data.ComplicationType primaryDataSourceDefaultType, android.content.ComponentName secondaryDataSource, androidx.wear.watchface.complications.data.ComplicationType secondaryDataSourceDefaultType, int systemDataSourceFallback, androidx.wear.watchface.complications.data.ComplicationType systemDataSourceFallbackDefaultType);
method public android.content.ComponentName? getPrimaryDataSource();
+ method public androidx.wear.watchface.complications.data.ComplicationType? getPrimaryDataSourceDefaultType();
method public android.content.ComponentName? getSecondaryDataSource();
+ method public androidx.wear.watchface.complications.data.ComplicationType? getSecondaryDataSourceDefaultType();
method public int getSystemDataSourceFallback();
+ method public androidx.wear.watchface.complications.data.ComplicationType getSystemDataSourceFallbackDefaultType();
method public boolean isEmpty();
property public final android.content.ComponentName? primaryDataSource;
+ property public final androidx.wear.watchface.complications.data.ComplicationType? primaryDataSourceDefaultType;
property public final android.content.ComponentName? secondaryDataSource;
+ property public final androidx.wear.watchface.complications.data.ComplicationType? secondaryDataSourceDefaultType;
property public final int systemDataSourceFallback;
+ property public final androidx.wear.watchface.complications.data.ComplicationType systemDataSourceFallbackDefaultType;
}
public final class SystemDataSources {
diff --git a/wear/watchface/watchface-complications/api/restricted_current.txt b/wear/watchface/watchface-complications/api/restricted_current.txt
index bf9465a..dbee437 100644
--- a/wear/watchface/watchface-complications/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications/api/restricted_current.txt
@@ -50,16 +50,25 @@
public final class DefaultComplicationDataSourcePolicy {
ctor public DefaultComplicationDataSourcePolicy();
- ctor public DefaultComplicationDataSourcePolicy(int systemProvider);
- ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName dataSource, int systemDataSourceFallback);
- ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, android.content.ComponentName secondaryDataSource, int systemDataSourceFallback);
+ ctor @Deprecated public DefaultComplicationDataSourcePolicy(int systemProvider);
+ ctor public DefaultComplicationDataSourcePolicy(int systemDataSource, androidx.wear.watchface.complications.data.ComplicationType systemDataSourceDefaultType);
+ ctor @Deprecated public DefaultComplicationDataSourcePolicy(android.content.ComponentName dataSource, int systemDataSourceFallback);
+ ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, androidx.wear.watchface.complications.data.ComplicationType primaryDataSourceDefaultType, int systemDataSourceFallback, androidx.wear.watchface.complications.data.ComplicationType systemDataSourceFallbackDefaultType);
+ ctor @Deprecated public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, android.content.ComponentName secondaryDataSource, int systemDataSourceFallback);
+ ctor public DefaultComplicationDataSourcePolicy(android.content.ComponentName primaryDataSource, androidx.wear.watchface.complications.data.ComplicationType primaryDataSourceDefaultType, android.content.ComponentName secondaryDataSource, androidx.wear.watchface.complications.data.ComplicationType secondaryDataSourceDefaultType, int systemDataSourceFallback, androidx.wear.watchface.complications.data.ComplicationType systemDataSourceFallbackDefaultType);
method public android.content.ComponentName? getPrimaryDataSource();
+ method public androidx.wear.watchface.complications.data.ComplicationType? getPrimaryDataSourceDefaultType();
method public android.content.ComponentName? getSecondaryDataSource();
+ method public androidx.wear.watchface.complications.data.ComplicationType? getSecondaryDataSourceDefaultType();
method public int getSystemDataSourceFallback();
+ method public androidx.wear.watchface.complications.data.ComplicationType getSystemDataSourceFallbackDefaultType();
method public boolean isEmpty();
property public final android.content.ComponentName? primaryDataSource;
+ property public final androidx.wear.watchface.complications.data.ComplicationType? primaryDataSourceDefaultType;
property public final android.content.ComponentName? secondaryDataSource;
+ property public final androidx.wear.watchface.complications.data.ComplicationType? secondaryDataSourceDefaultType;
property public final int systemDataSourceFallback;
+ property public final androidx.wear.watchface.complications.data.ComplicationType systemDataSourceFallbackDefaultType;
}
public final class SystemDataSources {
diff --git a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/DefaultComplicationDataSourcePolicy.kt b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/DefaultComplicationDataSourcePolicy.kt
index 7322082..a4137f3 100644
--- a/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/DefaultComplicationDataSourcePolicy.kt
+++ b/wear/watchface/watchface-complications/src/main/java/androidx/wear/watchface/complications/DefaultComplicationDataSourcePolicy.kt
@@ -19,6 +19,7 @@
import android.content.ComponentName
import androidx.annotation.RestrictTo
import androidx.wear.watchface.complications.SystemDataSources.DataSourceId
+import androidx.wear.watchface.complications.data.ComplicationType
import java.util.ArrayList
/**
@@ -34,51 +35,140 @@
/** First of two non-system data sources to be tried in turn. Set to null if not required. */
public val primaryDataSource: ComponentName?
+ /**
+ * The default [ComplicationType] for [primaryDataSource]. Note Pre-R this will be ignored in
+ * favour of [systemDataSourceFallbackDefaultType].
+ */
+ public val primaryDataSourceDefaultType: ComplicationType?
+
/** Second of two non-system data sources to be tried in turn. Set to null if not required. */
public val secondaryDataSource: ComponentName?
+ /**
+ * The default [ComplicationType] for [secondaryDataSource]. Note Pre-R this will be ignored in
+ * favour of [systemDataSourceFallbackDefaultType].
+ */
+ public val secondaryDataSourceDefaultType: ComplicationType?
+
/** Fallback in case none of the non-system data sources could be used. */
@DataSourceId
public val systemDataSourceFallback: Int
+ /** The default [ComplicationType] for [systemDataSourceFallback]. */
+ public val systemDataSourceFallbackDefaultType: ComplicationType
+
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public constructor(
dataSources: List<ComponentName>,
- @DataSourceId systemProviderFallback: Int
+ @DataSourceId systemDataSourceFallback: Int,
+ primaryDataSourceDefaultType: ComplicationType,
+ secondaryDataSourceDefaultType: ComplicationType,
+ systemDataSourceFallbackDefaultType: ComplicationType
) {
this.primaryDataSource = if (dataSources.isNotEmpty()) dataSources[0] else null
+ this.primaryDataSourceDefaultType = primaryDataSourceDefaultType
+
this.secondaryDataSource = if (dataSources.size >= 2) dataSources[1] else null
- this.systemDataSourceFallback = systemProviderFallback
+ this.secondaryDataSourceDefaultType = secondaryDataSourceDefaultType
+
+ this.systemDataSourceFallback = systemDataSourceFallback
+ this.systemDataSourceFallbackDefaultType =
+ systemDataSourceFallbackDefaultType
}
/** No default complication data source. */
public constructor() {
primaryDataSource = null
+ primaryDataSourceDefaultType = null
secondaryDataSource = null
+ secondaryDataSourceDefaultType = null
systemDataSourceFallback = NO_DEFAULT_PROVIDER
+ systemDataSourceFallbackDefaultType = ComplicationType.NOT_CONFIGURED
}
/**
- * Uses systemProvider as the default complication data source.
+ * Uses [systemProvider] as the default complication data source.
*/
+ @Deprecated(
+ "Use a constructor that sets the DefaultTypes",
+ ReplaceWith("DefaultComplicationDataSourcePolicy(Int, ComplicationType)")
+ )
public constructor(@DataSourceId systemProvider: Int) {
primaryDataSource = null
secondaryDataSource = null
systemDataSourceFallback = systemProvider
+ primaryDataSourceDefaultType = null
+ secondaryDataSourceDefaultType = null
+ systemDataSourceFallbackDefaultType = ComplicationType.NOT_CONFIGURED
+ }
+
+ /**
+ * Uses a system data source as the default complication data source.
+ *
+ * @param systemDataSource The system [DataSourceId] data source to use.
+ * @param systemDataSourceDefaultType The initial default [ComplicationType].
+ */
+ public constructor(
+ @DataSourceId systemDataSource: Int,
+ systemDataSourceDefaultType: ComplicationType
+ ) {
+ primaryDataSource = null
+ primaryDataSourceDefaultType = null
+ secondaryDataSource = null
+ secondaryDataSourceDefaultType = null
+ systemDataSourceFallback = systemDataSource
+ systemDataSourceFallbackDefaultType = systemDataSourceDefaultType
}
/**
* Attempts to use [dataSource] as the default complication complication data source, if not
* present then [systemDataSourceFallback] will be used instead.
*/
+ @Deprecated(
+ "Use a constructor that sets the DefaultTypes",
+ ReplaceWith(
+ "DefaultComplicationDataSourcePolicy(ComponentName, ComplicationType, Int," +
+ " ComplicationType)"
+ )
+ )
public constructor(
dataSource: ComponentName,
@DataSourceId systemDataSourceFallback: Int
) {
primaryDataSource = dataSource
+ primaryDataSourceDefaultType = null
secondaryDataSource = null
+ secondaryDataSourceDefaultType = null
this.systemDataSourceFallback = systemDataSourceFallback
+ systemDataSourceFallbackDefaultType = ComplicationType.NOT_CONFIGURED
+ }
+
+ /**
+ * Attempts to use [primaryDataSource] as the default complication complication data source, if
+ * not present then [systemDataSourceFallback] will be used instead.
+ *
+ * @param primaryDataSource The data source to try.
+ * @param primaryDataSourceDefaultType The default [ComplicationType] if
+ * primaryDataSource is selected. Note Pre-R this will be ignored in favour of
+ * [systemDataSourceFallbackDefaultType].
+ * @param systemDataSourceFallback The system data source to fall back on if neither provider is
+ * available.
+ * @param systemDataSourceFallbackDefaultType The default [ComplicationType] if
+ * systemDataSourceFallback is selected.
+ */
+ public constructor(
+ primaryDataSource: ComponentName,
+ primaryDataSourceDefaultType: ComplicationType,
+ @DataSourceId systemDataSourceFallback: Int,
+ systemDataSourceFallbackDefaultType: ComplicationType
+ ) {
+ this.primaryDataSource = primaryDataSource
+ this.primaryDataSourceDefaultType = primaryDataSourceDefaultType
+ secondaryDataSource = null
+ secondaryDataSourceDefaultType = null
+ this.systemDataSourceFallback = systemDataSourceFallback
+ this.systemDataSourceFallbackDefaultType = systemDataSourceFallbackDefaultType
}
/**
@@ -86,6 +176,13 @@
* then [secondaryDataSource] will be tried and if that's not present then
* [systemDataSourceFallback] will be used instead.
*/
+ @Deprecated(
+ "Use a constructor that sets the DefaultTypes",
+ ReplaceWith(
+ "DefaultComplicationDataSourcePolicy(ComponentName, ComplicationType, ComponentName, " +
+ "ComplicationType, Int, ComplicationType)"
+ )
+ )
public constructor(
primaryDataSource: ComponentName,
secondaryDataSource: ComponentName,
@@ -94,6 +191,43 @@
this.primaryDataSource = primaryDataSource
this.secondaryDataSource = secondaryDataSource
this.systemDataSourceFallback = systemDataSourceFallback
+ primaryDataSourceDefaultType = null
+ secondaryDataSourceDefaultType = null
+ systemDataSourceFallbackDefaultType = ComplicationType.NOT_CONFIGURED
+ }
+
+ /**
+ * Attempts to use [primaryDataSource] as the default complication data source, if not present
+ * then [secondaryDataSource] will be tried and if that's not present then
+ * [systemDataSourceFallback] will be used instead.
+ *
+ * @param primaryDataSource The first data source to try.
+ * @param primaryDataSourceDefaultType The default [ComplicationType] if
+ * primaryDataSource is selected. Note Pre-R this will be ignored in favour of
+ * [systemDataSourceFallbackDefaultType].
+ * @param secondaryDataSource The second data source to try.
+ * @param secondaryDataSourceDefaultType The default [ComplicationType] if
+ * secondaryDataSource is selected. Note Pre-R this will be ignored in favour of
+ * [systemDataSourceFallbackDefaultType].
+ * @param systemDataSourceFallback The system data source to fall back on if neither provider is
+ * available.
+ * @param systemDataSourceFallbackDefaultType The default [ComplicationType] if
+ * systemDataSourceFallback is selected.
+ */
+ public constructor(
+ primaryDataSource: ComponentName,
+ primaryDataSourceDefaultType: ComplicationType,
+ secondaryDataSource: ComponentName,
+ secondaryDataSourceDefaultType: ComplicationType,
+ @DataSourceId systemDataSourceFallback: Int,
+ systemDataSourceFallbackDefaultType: ComplicationType
+ ) {
+ this.primaryDataSource = primaryDataSource
+ this.primaryDataSourceDefaultType = primaryDataSourceDefaultType
+ this.secondaryDataSource = secondaryDataSource
+ this.secondaryDataSourceDefaultType = secondaryDataSourceDefaultType
+ this.systemDataSourceFallback = systemDataSourceFallback
+ this.systemDataSourceFallbackDefaultType = systemDataSourceFallbackDefaultType
}
/** Whether or not this DefaultComplicationDataSourcePolicy contains a default data source. */
diff --git a/wear/watchface/watchface-data/api/restricted_current.txt b/wear/watchface/watchface-data/api/restricted_current.txt
index 9342af9..5b22113 100644
--- a/wear/watchface/watchface-data/api/restricted_current.txt
+++ b/wear/watchface/watchface-data/api/restricted_current.txt
@@ -97,7 +97,8 @@
package androidx.wear.watchface.data {
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.VersionedParcelize public final class ComplicationSlotMetadataWireFormat implements android.os.Parcelable androidx.versionedparcelable.VersionedParcelable {
- ctor public ComplicationSlotMetadataWireFormat(int, int[], android.graphics.RectF![], int, @android.support.wearable.complications.ComplicationData.ComplicationType int[], java.util.List<android.content.ComponentName!>?, int, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean, boolean, android.os.Bundle);
+ ctor public ComplicationSlotMetadataWireFormat(int, int[], android.graphics.RectF![], int, @android.support.wearable.complications.ComplicationData.ComplicationType int[], java.util.List<android.content.ComponentName!>?, int, @android.support.wearable.complications.ComplicationData.ComplicationType int, @android.support.wearable.complications.ComplicationData.ComplicationType int, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean, boolean, android.os.Bundle);
+ ctor @Deprecated public ComplicationSlotMetadataWireFormat(int, int[], android.graphics.RectF![], int, @android.support.wearable.complications.ComplicationData.ComplicationType int[], java.util.List<android.content.ComponentName!>?, int, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean, boolean, android.os.Bundle);
method public int describeContents();
method public int getBoundsType();
method public android.graphics.RectF![] getComplicationBounds();
@@ -107,6 +108,8 @@
method public java.util.List<android.content.ComponentName!>? getDefaultDataSourcesToTry();
method public int getFallbackSystemDataSource();
method public int getId();
+ method @android.support.wearable.complications.ComplicationData.ComplicationType public int getPrimaryDataSourceDefaultType();
+ method @android.support.wearable.complications.ComplicationData.ComplicationType public int getSecondaryDataSourceDefaultType();
method @android.support.wearable.complications.ComplicationData.ComplicationType public int[] getSupportedTypes();
method public boolean isFixedComplicationDataSource();
method public boolean isInitiallyEnabled();
@@ -115,15 +118,20 @@
}
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @androidx.versionedparcelable.VersionedParcelize public final class ComplicationStateWireFormat implements android.os.Parcelable androidx.versionedparcelable.VersionedParcelable {
- ctor public ComplicationStateWireFormat(android.graphics.Rect, int, @android.support.wearable.complications.ComplicationData.ComplicationType int[], java.util.List<android.content.ComponentName!>?, int, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean, boolean, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean, android.os.Bundle);
+ ctor public ComplicationStateWireFormat(android.graphics.Rect, int, @android.support.wearable.complications.ComplicationData.ComplicationType int[], java.util.List<android.content.ComponentName!>?, int, @android.support.wearable.complications.ComplicationData.ComplicationType int, @android.support.wearable.complications.ComplicationData.ComplicationType int, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean, boolean, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean, android.os.Bundle);
+ ctor @Deprecated public ComplicationStateWireFormat(android.graphics.Rect, int, @android.support.wearable.complications.ComplicationData.ComplicationType int[], java.util.List<android.content.ComponentName!>?, int, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean, boolean, @android.support.wearable.complications.ComplicationData.ComplicationType int, boolean, android.os.Bundle);
method public int describeContents();
method public android.graphics.Rect getBounds();
method public int getBoundsType();
method public android.os.Bundle getComplicationConfigExtras();
method @android.support.wearable.complications.ComplicationData.ComplicationType public int getCurrentType();
- method @android.support.wearable.complications.ComplicationData.ComplicationType public int getDefaultProviderType();
- method public java.util.List<android.content.ComponentName!>? getDefaultProvidersToTry();
+ method @android.support.wearable.complications.ComplicationData.ComplicationType public int getDefaultDataSourceType();
+ method public java.util.List<android.content.ComponentName!>? getDefaultDataSourcesToTry();
+ method @Deprecated @android.support.wearable.complications.ComplicationData.ComplicationType public int getDefaultProviderType();
+ method @Deprecated public java.util.List<android.content.ComponentName!>? getDefaultProvidersToTry();
method public int getFallbackSystemProvider();
+ method @android.support.wearable.complications.ComplicationData.ComplicationType public int getPrimaryDataSourceDefaultType();
+ method @android.support.wearable.complications.ComplicationData.ComplicationType public int getSecondaryDataSourceDefaultType();
method @android.support.wearable.complications.ComplicationData.ComplicationType public int[] getSupportedTypes();
method public boolean isEnabled();
method public boolean isFixedComplicationProvider();
diff --git a/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationSlotMetadataWireFormat.java b/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationSlotMetadataWireFormat.java
index ed4375f..ec0cc13 100644
--- a/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationSlotMetadataWireFormat.java
+++ b/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationSlotMetadataWireFormat.java
@@ -68,7 +68,7 @@
@ParcelField(8)
@ComplicationData.ComplicationType
- int mDefaultDataSourceType;
+ int mDefaultType;
@ParcelField(9)
boolean mIsInitiallyEnabled;
@@ -80,6 +80,16 @@
@NonNull
Bundle mComplicationConfigExtras;
+ // Not supported in library v1.0.
+ @ParcelField(12)
+ @ComplicationData.ComplicationType
+ int mPrimaryDataSourceDefaultType = ComplicationData.TYPE_NOT_CONFIGURED;
+
+ // Not supported in library v1.0.
+ @ParcelField(13)
+ @ComplicationData.ComplicationType
+ int mSecondaryDataSourceDefaultType = ComplicationData.TYPE_NOT_CONFIGURED;
+
/** Used by VersionedParcelable. */
ComplicationSlotMetadataWireFormat() {
}
@@ -93,6 +103,40 @@
@Nullable List<ComponentName> defaultDataSourcesToTry,
int fallbackSystemDataSource,
@ComplicationData.ComplicationType int defaultDataSourceType,
+ @ComplicationData.ComplicationType int primaryDataSourceDefaultType,
+ @ComplicationData.ComplicationType int secondaryDataSourceDefaultType,
+ boolean isInitiallyEnabled,
+ boolean fixedComplicationDataSource,
+ @NonNull Bundle complicationConfigExtras) {
+ mId = id;
+ mComplicationBoundsType = complicationBoundsType;
+ mComplicationBounds = complicationBounds;
+ mBoundsType = boundsType;
+ mSupportedTypes = supportedTypes;
+ mDefaultDataSourcesToTry = defaultDataSourcesToTry;
+ mPrimaryDataSourceDefaultType = primaryDataSourceDefaultType;
+ mSecondaryDataSourceDefaultType = secondaryDataSourceDefaultType;
+ mFallbackSystemDataSource = fallbackSystemDataSource;
+ mDefaultType = defaultDataSourceType;
+ mIsInitiallyEnabled = isInitiallyEnabled;
+ mFixedComplicationDataSource = fixedComplicationDataSource;
+ mComplicationConfigExtras = complicationConfigExtras;
+ }
+
+ /**
+ * @deprecated Use the other constructor with primaryDataSourceDefaultType &
+ * secondaryDataSourceDefaultType instead.
+ */
+ @Deprecated
+ public ComplicationSlotMetadataWireFormat(
+ int id,
+ @NonNull int[] complicationBoundsType,
+ @NonNull RectF[] complicationBounds,
+ int boundsType,
+ @NonNull @ComplicationData.ComplicationType int[] supportedTypes,
+ @Nullable List<ComponentName> defaultDataSourcesToTry,
+ int fallbackSystemDataSource,
+ @ComplicationData.ComplicationType int defaultDataSourceType,
boolean isInitiallyEnabled,
boolean fixedComplicationDataSource,
@NonNull Bundle complicationConfigExtras) {
@@ -103,7 +147,7 @@
mSupportedTypes = supportedTypes;
mDefaultDataSourcesToTry = defaultDataSourcesToTry;
mFallbackSystemDataSource = fallbackSystemDataSource;
- mDefaultDataSourceType = defaultDataSourceType;
+ mDefaultType = defaultDataSourceType;
mIsInitiallyEnabled = isInitiallyEnabled;
mFixedComplicationDataSource = fixedComplicationDataSource;
mComplicationConfigExtras = complicationConfigExtras;
@@ -150,9 +194,37 @@
return mFallbackSystemDataSource;
}
+ /**
+ * @return The {@link ComplicationData.ComplicationType} for
+ * {@link #getFallbackSystemDataSource}.
+ */
@ComplicationData.ComplicationType
public int getDefaultDataSourceType() {
- return mDefaultDataSourceType;
+ return mDefaultType;
+ }
+
+ /**
+ * @return The {@link ComplicationData.ComplicationType} for the first entry from
+ * {@link #getDefaultDataSourcesToTry}.
+ */
+ @ComplicationData.ComplicationType
+ public int getPrimaryDataSourceDefaultType() {
+ // Not supported in library v1.0. TYPE_NOT_CONFIGURED is not a valid API choice indicating
+ // and old client.
+ return (mPrimaryDataSourceDefaultType == ComplicationData.TYPE_NOT_CONFIGURED)
+ ? mDefaultType : mPrimaryDataSourceDefaultType;
+ }
+
+ /**
+ * @return The {@link ComplicationData.ComplicationType} for the second entry from
+ * {@link #getDefaultDataSourcesToTry}.
+ */
+ @ComplicationData.ComplicationType
+ public int getSecondaryDataSourceDefaultType() {
+ // Not supported in library v1.0. TYPE_NOT_CONFIGURED is not a valid API choice indicating
+ // and old client.
+ return (mSecondaryDataSourceDefaultType == ComplicationData.TYPE_NOT_CONFIGURED)
+ ? mDefaultType : mSecondaryDataSourceDefaultType;
}
public boolean isInitiallyEnabled() {
diff --git a/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java b/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java
index e9c9ffd..56fa456 100644
--- a/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java
+++ b/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/data/ComplicationStateWireFormat.java
@@ -57,14 +57,14 @@
@ParcelField(4)
@Nullable
- List<ComponentName> mDefaultProvidersToTry;
+ List<ComponentName> mDefaultDataSourcesToTry;
@ParcelField(5)
int mFallbackSystemProvider;
@ParcelField(6)
@ComplicationData.ComplicationType
- int mDefaultProviderType;
+ int mDefaultType;
@ParcelField(7)
boolean mIsEnabled;
@@ -83,6 +83,16 @@
@NonNull
Bundle mComplicationConfigExtras;
+ // Not supported in library v1.0.
+ @ParcelField(12)
+ @ComplicationData.ComplicationType
+ int mPrimaryDataSourceDefaultType = ComplicationData.TYPE_NOT_CONFIGURED;
+
+ // Not supported in library v1.0.
+ @ParcelField(13)
+ @ComplicationData.ComplicationType
+ int mSecondaryDataSourceDefaultType = ComplicationData.TYPE_NOT_CONFIGURED;
+
/** Used by VersionedParcelable. */
ComplicationStateWireFormat() {
}
@@ -91,9 +101,11 @@
@NonNull Rect bounds,
int boundsType,
@NonNull @ComplicationData.ComplicationType int[] supportedTypes,
- @Nullable List<ComponentName> defaultProvidersToTry,
+ @Nullable List<ComponentName> defaultDataSourcesToTry,
int fallbackSystemProvider,
- @ComplicationData.ComplicationType int defaultProviderType,
+ @ComplicationData.ComplicationType int defaultDataSourceType,
+ @ComplicationData.ComplicationType int primaryDataSourceDefaultType,
+ @ComplicationData.ComplicationType int secondaryDataSourceDefaultType,
boolean isEnabled,
boolean isInitiallyEnabled,
@ComplicationData.ComplicationType int currentType,
@@ -102,9 +114,38 @@
mBounds = bounds;
mBoundsType = boundsType;
mSupportedTypes = supportedTypes;
- mDefaultProvidersToTry = defaultProvidersToTry;
+ mDefaultDataSourcesToTry = defaultDataSourcesToTry;
mFallbackSystemProvider = fallbackSystemProvider;
- mDefaultProviderType = defaultProviderType;
+ mDefaultType = defaultDataSourceType;
+ mPrimaryDataSourceDefaultType = primaryDataSourceDefaultType;
+ mSecondaryDataSourceDefaultType = secondaryDataSourceDefaultType;
+ mIsEnabled = isEnabled;
+ mIsInitiallyEnabled = isInitiallyEnabled;
+ mCurrentType = currentType;
+ mFixedComplicationProvider = fixedComplicationProvider;
+ mComplicationConfigExtras = complicationConfigExtras;
+ }
+
+ /** @deprecated Use the other constructor instead. */
+ @Deprecated
+ public ComplicationStateWireFormat(
+ @NonNull Rect bounds,
+ int boundsType,
+ @NonNull @ComplicationData.ComplicationType int[] supportedTypes,
+ @Nullable List<ComponentName> defaultDataSourcesToTry,
+ int fallbackSystemProvider,
+ @ComplicationData.ComplicationType int defaultDataSourceType,
+ boolean isEnabled,
+ boolean isInitiallyEnabled,
+ @ComplicationData.ComplicationType int currentType,
+ boolean fixedComplicationProvider,
+ @NonNull Bundle complicationConfigExtras) {
+ mBounds = bounds;
+ mBoundsType = boundsType;
+ mSupportedTypes = supportedTypes;
+ mDefaultDataSourcesToTry = defaultDataSourcesToTry;
+ mFallbackSystemProvider = fallbackSystemProvider;
+ mDefaultType = defaultDataSourceType;
mIsEnabled = isEnabled;
mIsInitiallyEnabled = isInitiallyEnabled;
mCurrentType = currentType;
@@ -130,23 +171,68 @@
/**
* Along with {@link #getFallbackSystemProvider} this is the wire format for
* DefaultComplicationDataSourcePolicy.
+ * @deprecated Use {@link #getDefaultDataSourcesToTry} instead.
*/
+ @Deprecated
@Nullable
public List<ComponentName> getDefaultProvidersToTry() {
- return mDefaultProvidersToTry;
+ return mDefaultDataSourcesToTry;
}
/**
- * Along with {@link #getDefaultProvidersToTry} this is the wire format for
+ * Along with {@link #getFallbackSystemProvider} this is the wire format for
+ * DefaultComplicationDataSourcePolicy.
+ */
+ @Nullable
+ public List<ComponentName> getDefaultDataSourcesToTry() {
+ return mDefaultDataSourcesToTry;
+ }
+
+ /**
+ * Along with {@link #getDefaultDataSourcesToTry} this is the wire format for
* DefaultComplicationDataSourcePolicy.
*/
public int getFallbackSystemProvider() {
return mFallbackSystemProvider;
}
+ /** @deprecated Use {@link #getDefaultDataSourceType} instead. */
+ @Deprecated
@ComplicationData.ComplicationType
public int getDefaultProviderType() {
- return mDefaultProviderType;
+ return mDefaultType;
+ }
+
+ /**
+ * @return The {@link ComplicationData.ComplicationType} for {@link #getFallbackSystemProvider}.
+ */
+ @ComplicationData.ComplicationType
+ public int getDefaultDataSourceType() {
+ return mDefaultType;
+ }
+
+ /**
+ * @return The {@link ComplicationData.ComplicationType} for the first entry from
+ * {@link #getDefaultDataSourcesToTry}.
+ */
+ @ComplicationData.ComplicationType
+ public int getPrimaryDataSourceDefaultType() {
+ // Not supported in library v1.0. TYPE_NOT_CONFIGURED is not a valid API choice indicating
+ // and old client.
+ return (mPrimaryDataSourceDefaultType == ComplicationData.TYPE_NOT_CONFIGURED)
+ ? mDefaultType : mPrimaryDataSourceDefaultType;
+ }
+
+ /**
+ * @return The {@link ComplicationData.ComplicationType} for the second entry from
+ * {@link #getDefaultDataSourcesToTry}.
+ */
+ @ComplicationData.ComplicationType
+ public int getSecondaryDataSourceDefaultType() {
+ // Not supported in library v1.0. TYPE_NOT_CONFIGURED is not a valid API choice indicating
+ // and old client.
+ return (mSecondaryDataSourceDefaultType == ComplicationData.TYPE_NOT_CONFIGURED)
+ ? mDefaultType : mSecondaryDataSourceDefaultType;
}
public boolean isEnabled() {
@@ -161,7 +247,6 @@
return mFixedComplicationProvider;
}
- @NonNull
@ComplicationData.ComplicationType
public int getCurrentType() {
return mCurrentType;
diff --git a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionGuavaTest.kt b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionGuavaTest.kt
index 6cff617..45ed5db 100644
--- a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionGuavaTest.kt
+++ b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionGuavaTest.kt
@@ -81,6 +81,7 @@
mockInvalidateCallback
)
private val leftComplication =
+ @Suppress("DEPRECATION")
ComplicationSlot.createRoundRectComplicationSlotBuilder(
LEFT_COMPLICATION_ID,
{ _, _, -> mockLeftCanvasComplication },
@@ -103,6 +104,7 @@
mockInvalidateCallback
)
private val rightComplication =
+ @Suppress("DEPRECATION")
ComplicationSlot.createRoundRectComplicationSlotBuilder(
RIGHT_COMPLICATION_ID,
{ _, _, -> mockRightCanvasComplication },
diff --git a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
index e874449..54abaae 100644
--- a/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
+++ b/wear/watchface/watchface-editor/src/androidTest/java/androidx/wear/watchface/editor/EditorSessionTest.kt
@@ -34,6 +34,7 @@
import android.os.Handler
import android.os.HandlerThread
import android.os.IBinder
+import android.os.Looper
import android.support.wearable.complications.IPreviewComplicationDataCallback
import android.support.wearable.complications.IProviderInfoService
import android.support.wearable.watchface.Constants
@@ -485,7 +486,13 @@
ComplicationType.MONOCHROMATIC_IMAGE,
ComplicationType.SMALL_IMAGE
),
- DefaultComplicationDataSourcePolicy(SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET),
+
+ DefaultComplicationDataSourcePolicy(
+ ComponentName("com.primary.package", "com.primary.app"),
+ ComplicationType.LONG_TEXT,
+ SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET,
+ ComplicationType.SHORT_TEXT
+ ),
ComplicationSlotBounds(
ComplicationType.values().associateWith {
if (it == ComplicationType.LONG_TEXT) {
@@ -495,7 +502,7 @@
}
}
)
- ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
+ )
.build()
private val rightComplication =
@@ -509,7 +516,14 @@
ComplicationType.MONOCHROMATIC_IMAGE,
ComplicationType.SMALL_IMAGE
),
- DefaultComplicationDataSourcePolicy(SystemDataSources.DATA_SOURCE_DAY_OF_WEEK),
+ DefaultComplicationDataSourcePolicy(
+ ComponentName("com.primary.package", "com.primary.app"),
+ ComplicationType.LONG_TEXT,
+ ComponentName("com.secondary.package", "com.secondary.app"),
+ ComplicationType.PHOTO_IMAGE,
+ SystemDataSources.DATA_SOURCE_DAY_OF_WEEK,
+ ComplicationType.SHORT_TEXT
+ ),
ComplicationSlotBounds(
ComplicationType.values().associateWith {
if (it == ComplicationType.LONG_TEXT) {
@@ -519,7 +533,7 @@
}
}
)
- ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
+ )
.setConfigExtras(
Bundle().apply {
putString(PROVIDER_CHOOSER_EXTRA_KEY, PROVIDER_CHOOSER_EXTRA_VALUE)
@@ -583,6 +597,12 @@
onDestroyLatch.countDown()
backgroundHandlerThread.quitSafely()
}
+
+ override fun setComplicationSlotConfigExtrasChangeCallback(
+ callback: WatchFace.ComplicationSlotConfigExtrasChangeCallback?
+ ) {
+ complicationSlotsManager.configExtrasChangeCallback = callback
+ }
}
if (!shouldTimeout) {
WatchFace.registerEditorDelegate(watchComponentName, editorDelegate)
@@ -758,6 +778,7 @@
}
@Test
+ @Suppress("DEPRECATION") // defaultDataSourceType
public fun complicationState() {
val scenario = createOnWatchFaceEditingTestActivity(
emptyList(),
@@ -766,40 +787,54 @@
scenario.onActivity {
val complicationSlotsState = it.editorSession.complicationSlotsState.value
assertThat(complicationSlotsState.size).isEqualTo(3)
- assertThat(complicationSlotsState[LEFT_COMPLICATION_ID]!!.bounds)
- .isEqualTo(Rect(120, 160, 160, 240))
- assertThat(complicationSlotsState[LEFT_COMPLICATION_ID]!!.boundsType)
- .isEqualTo(ComplicationSlotBoundsType.ROUND_RECT)
- assertFalse(
- complicationSlotsState[LEFT_COMPLICATION_ID]!!.fixedComplicationDataSource
+ val leftSlot = complicationSlotsState[LEFT_COMPLICATION_ID]!!
+ assertThat(leftSlot.bounds).isEqualTo(Rect(120, 160, 160, 240))
+ assertThat(leftSlot.boundsType).isEqualTo(ComplicationSlotBoundsType.ROUND_RECT)
+ assertFalse(leftSlot.fixedComplicationDataSource)
+ assertTrue(leftSlot.isInitiallyEnabled)
+ assertThat(leftSlot.defaultDataSourcePolicy.primaryDataSource)
+ .isEqualTo(ComponentName("com.primary.package", "com.primary.app"))
+ assertThat(leftSlot.defaultDataSourcePolicy.primaryDataSourceDefaultType)
+ .isEqualTo(ComplicationType.LONG_TEXT)
+ assertThat(leftSlot.defaultDataSourcePolicy.secondaryDataSource).isNull()
+ assertThat(leftSlot.defaultDataSourcePolicy.secondaryDataSourceDefaultType)
+ .isNull()
+ assertThat(leftSlot.defaultDataSourcePolicy.systemDataSourceFallback).isEqualTo(
+ SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET
)
- assertTrue(
- complicationSlotsState[LEFT_COMPLICATION_ID]!!.isInitiallyEnabled
- )
+ assertThat(
+ leftSlot.defaultDataSourcePolicy.systemDataSourceFallbackDefaultType
+ ).isEqualTo(ComplicationType.SHORT_TEXT)
+ assertThat(leftSlot.defaultDataSourceType).isEqualTo(ComplicationType.SHORT_TEXT)
- assertThat(complicationSlotsState[RIGHT_COMPLICATION_ID]!!.bounds)
- .isEqualTo(Rect(240, 160, 280, 240))
- assertThat(complicationSlotsState[RIGHT_COMPLICATION_ID]!!.boundsType)
- .isEqualTo(ComplicationSlotBoundsType.ROUND_RECT)
- assertFalse(
- complicationSlotsState[RIGHT_COMPLICATION_ID]!!.fixedComplicationDataSource
- )
- assertTrue(complicationSlotsState[RIGHT_COMPLICATION_ID]!!.isInitiallyEnabled)
+ val rightSlot = complicationSlotsState[RIGHT_COMPLICATION_ID]!!
+ assertThat(rightSlot.bounds).isEqualTo(Rect(240, 160, 280, 240))
+ assertThat(rightSlot.boundsType).isEqualTo(ComplicationSlotBoundsType.ROUND_RECT)
+ assertFalse(rightSlot.fixedComplicationDataSource)
+ assertTrue(rightSlot.isInitiallyEnabled)
+ assertThat(rightSlot.defaultDataSourcePolicy.primaryDataSource)
+ .isEqualTo(ComponentName("com.primary.package", "com.primary.app"))
+ assertThat(rightSlot.defaultDataSourcePolicy.primaryDataSourceDefaultType)
+ .isEqualTo(ComplicationType.LONG_TEXT)
+ assertThat(rightSlot.defaultDataSourcePolicy.secondaryDataSource)
+ .isEqualTo(ComponentName("com.secondary.package", "com.secondary.app"))
+ assertThat(rightSlot.defaultDataSourcePolicy.secondaryDataSourceDefaultType)
+ .isEqualTo(ComplicationType.PHOTO_IMAGE)
+ assertThat(
+ rightSlot.defaultDataSourcePolicy.systemDataSourceFallbackDefaultType
+ ).isEqualTo(ComplicationType.SHORT_TEXT)
+ assertThat(rightSlot.defaultDataSourceType).isEqualTo(ComplicationType.SHORT_TEXT)
- assertThat(complicationSlotsState[BACKGROUND_COMPLICATION_ID]!!.bounds)
- .isEqualTo(screenBounds)
- assertThat(complicationSlotsState[BACKGROUND_COMPLICATION_ID]!!.boundsType)
- .isEqualTo(ComplicationSlotBoundsType.BACKGROUND)
- assertFalse(
- complicationSlotsState[BACKGROUND_COMPLICATION_ID]!!.fixedComplicationDataSource
- )
- assertFalse(
- complicationSlotsState[BACKGROUND_COMPLICATION_ID]!!.isInitiallyEnabled
- )
+ val backgroundSlot = complicationSlotsState[BACKGROUND_COMPLICATION_ID]!!
+ assertThat(backgroundSlot.bounds).isEqualTo(screenBounds)
+ assertThat(backgroundSlot.boundsType).isEqualTo(ComplicationSlotBoundsType.BACKGROUND)
+ assertFalse(backgroundSlot.fixedComplicationDataSource)
+ assertFalse(backgroundSlot.isInitiallyEnabled)
// We could test more state but this should be enough.
}
}
+ @Suppress("DEPRECATION") // Old DefaultComplicationDataSourcePolicy constructor
@Test
public fun fixedComplicationDataSource() {
val mockLeftCanvasComplication =
@@ -1440,6 +1475,57 @@
}
@Test
+ public fun mutable_configExtras() {
+ // Invoke the test data source chooser to record the result.
+ ComplicationHelperActivity.useTestComplicationDataSourceChooserActivity = true
+ // Invoke the data source chooser without checking for permissions first.
+ ComplicationHelperActivity.skipPermissionCheck = true
+
+ val chosenComplicationDataSourceInfo = ComplicationDataSourceInfo(
+ "TestDataSource3App",
+ "TestDataSource3",
+ Icon.createWithBitmap(
+ Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+ ),
+ ComplicationType.LONG_TEXT,
+ dataSource3
+ )
+ TestComplicationDataSourceChooserActivity.lastIntent = null
+ TestComplicationDataSourceChooserActivity.resultIntent = Intent().apply {
+ putExtra(
+ ComplicationDataSourceChooserIntent.EXTRA_PROVIDER_INFO,
+ chosenComplicationDataSourceInfo.toWireComplicationProviderInfo()
+ )
+ }
+
+ val scenario = createOnWatchFaceEditingTestActivity(
+ emptyList(),
+ listOf(leftComplication, rightComplication)
+ )
+
+ lateinit var editorSession: EditorSession
+ scenario.onActivity { activity ->
+ editorSession = activity.editorSession
+ }
+
+ runBlocking {
+ rightComplication.configExtras = Bundle().apply {
+ putString(PROVIDER_CHOOSER_EXTRA_KEY, "Updated")
+ }
+
+ val chosenComplicationDataSource =
+ editorSession.openComplicationDataSourceChooser(RIGHT_COMPLICATION_ID)
+ assertThat(chosenComplicationDataSource).isNotNull()
+
+ assertThat(
+ TestComplicationDataSourceChooserActivity.lastIntent?.extras?.getString(
+ PROVIDER_CHOOSER_EXTRA_KEY
+ )
+ ).isEqualTo("Updated")
+ }
+ }
+
+ @Test
public fun getComplicationIdAt() {
val scenario = createOnWatchFaceEditingTestActivity(
emptyList(),
@@ -2118,8 +2204,8 @@
scenario.onActivity { activity ->
val mockWatchFaceHostApi = mock(WatchFaceHostApi::class.java)
- val mockHandler = mock(Handler::class.java)
- `when`(mockWatchFaceHostApi.getUiThreadHandler()).thenReturn(mockHandler)
+ val handler = Handler(Looper.myLooper()!!)
+ `when`(mockWatchFaceHostApi.getUiThreadHandler()).thenReturn(handler)
`when`(mockWatchFaceHostApi.getContext()).thenReturn(
ApplicationProvider.getApplicationContext<Context>()
)
@@ -2165,7 +2251,7 @@
watchState,
mockWatchFaceHostApi,
CompletableDeferred(),
- CoroutineScope(mockHandler.asCoroutineDispatcher())
+ CoroutineScope(handler.asCoroutineDispatcher())
),
null
)
diff --git a/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt b/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
index ab7bdf8..03bca3c 100644
--- a/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
+++ b/wear/watchface/watchface-editor/src/main/java/androidx/wear/watchface/editor/EditorSession.kt
@@ -788,7 +788,6 @@
it.value.boundsType,
it.value.supportedTypes,
it.value.defaultDataSourcePolicy,
- it.value.defaultDataSourceType,
it.value.enabled,
it.value.initiallyEnabled,
it.value.renderer.getData().type,
@@ -889,6 +888,7 @@
// Note this has to be done last to ensure tests are not racy.
if (this::editorDelegate.isInitialized) {
+ editorDelegate.setComplicationSlotConfigExtrasChangeCallback(null)
editorDelegate.onDestroy()
}
}
@@ -910,6 +910,14 @@
)
fetchComplicationsDataJob = fetchComplicationsData(backgroundCoroutineScope)
+
+ editorDelegate.setComplicationSlotConfigExtrasChangeCallback(
+ object : WatchFace.ComplicationSlotConfigExtrasChangeCallback {
+ override fun onComplicationSlotConfigExtrasChanged() {
+ maybeUpdateComplicationSlotsState()
+ }
+ }
+ )
}
override val showComplicationDeniedDialogIntent
diff --git a/wear/watchface/watchface-guava/src/androidTest/res/xml/xml_watchface.xml b/wear/watchface/watchface-guava/src/androidTest/res/xml/xml_watchface.xml
index d32e161..12bf5f7 100644
--- a/wear/watchface/watchface-guava/src/androidTest/res/xml/xml_watchface.xml
+++ b/wear/watchface/watchface-guava/src/androidTest/res/xml/xml_watchface.xml
@@ -33,17 +33,18 @@
app:slotId="10"
app:boundsType="ROUND_RECT"
app:supportedTypes="SHORT_TEXT|RANGED_VALUE|SMALL_IMAGE"
- app:defaultDataSourceType="RANGED_VALUE"
- app:systemDataSourceFallback="DATA_SOURCE_WATCH_BATTERY">
+ app:systemDataSourceFallback="DATA_SOURCE_WATCH_BATTERY"
+ app:systemDataSourceFallbackDefaultType="RANGED_VALUE">
<ComplicationSlotBounds app:left="0.3" app:top="0.7" app:right="0.7" app:bottom="0.9"/>
</ComplicationSlot>
<ComplicationSlot
app:slotId="20"
app:boundsType="BACKGROUND"
app:supportedTypes="LONG_TEXT"
- app:defaultDataSourceType="LONG_TEXT"
app:primaryDataSource="com.package/com.app"
- app:systemDataSourceFallback="DATA_SOURCE_SUNRISE_SUNSET">
+ app:primaryDataSourceDefaultType="LONG_TEXT"
+ app:systemDataSourceFallback="DATA_SOURCE_SUNRISE_SUNSET"
+ app:systemDataSourceFallbackDefaultType="LONG_TEXT">
<ComplicationSlotBounds app:left="0.1" app:top="0.2" app:right="0.3" app:bottom="0.4"/>
</ComplicationSlot>
</XmlWatchFace>
\ No newline at end of file
diff --git a/wear/watchface/watchface-samples-minimal-complications/src/main/res/xml/xml_watchface.xml b/wear/watchface/watchface-samples-minimal-complications/src/main/res/xml/xml_watchface.xml
index 0113968..4bfae43 100644
--- a/wear/watchface/watchface-samples-minimal-complications/src/main/res/xml/xml_watchface.xml
+++ b/wear/watchface/watchface-samples-minimal-complications/src/main/res/xml/xml_watchface.xml
@@ -19,8 +19,8 @@
app:slotId="1"
app:boundsType="ROUND_RECT"
app:supportedTypes="SHORT_TEXT|RANGED_VALUE|SMALL_IMAGE"
- app:defaultDataSourceType="RANGED_VALUE"
- app:systemDataSourceFallback="DATA_SOURCE_WATCH_BATTERY">
+ app:systemDataSourceFallback="DATA_SOURCE_WATCH_BATTERY"
+ app:systemDataSourceFallbackDefaultType="RANGED_VALUE">
<ComplicationSlotBounds app:left="0.3" app:top="0.7" app:right="0.7" app:bottom="0.9"/>
</ComplicationSlot>
</XmlWatchFace>
diff --git a/wear/watchface/watchface/api/current.txt b/wear/watchface/watchface/api/current.txt
index a8af996..1685908 100644
--- a/wear/watchface/watchface/api/current.txt
+++ b/wear/watchface/watchface/api/current.txt
@@ -34,7 +34,7 @@
method @UiThread public androidx.wear.watchface.complications.ComplicationSlotBounds getComplicationSlotBounds();
method public android.os.Bundle getConfigExtras();
method @UiThread public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getDefaultDataSourcePolicy();
- method @UiThread public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
+ method @Deprecated @UiThread public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
method public boolean getFixedComplicationDataSource();
method public int getId();
method public boolean getInitiallyEnabled();
@@ -45,6 +45,7 @@
method @UiThread public boolean isEnabled();
method @UiThread public void render(android.graphics.Canvas canvas, java.time.ZonedDateTime zonedDateTime, androidx.wear.watchface.RenderParameters renderParameters);
method @UiThread public void renderHighlightLayer(android.graphics.Canvas canvas, java.time.ZonedDateTime zonedDateTime, androidx.wear.watchface.RenderParameters renderParameters);
+ method public void setConfigExtras(android.os.Bundle value);
property @UiThread public final int accessibilityTraversalIndex;
property public final int boundsType;
property public final androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory;
@@ -52,7 +53,7 @@
property @UiThread public final androidx.wear.watchface.complications.ComplicationSlotBounds complicationSlotBounds;
property public final android.os.Bundle configExtras;
property @UiThread public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy;
- property @UiThread public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
+ property @Deprecated @UiThread public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
property @UiThread public final boolean enabled;
property public final boolean fixedComplicationDataSource;
property public final int id;
@@ -67,7 +68,7 @@
method public androidx.wear.watchface.ComplicationSlot build();
method public androidx.wear.watchface.ComplicationSlot.Builder setAccessibilityTraversalIndex(int accessibilityTraversalIndex);
method public androidx.wear.watchface.ComplicationSlot.Builder setConfigExtras(android.os.Bundle extras);
- method public androidx.wear.watchface.ComplicationSlot.Builder setDefaultDataSourceType(androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType);
+ method @Deprecated public androidx.wear.watchface.ComplicationSlot.Builder setDefaultDataSourceType(androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType);
method public androidx.wear.watchface.ComplicationSlot.Builder setEnabled(boolean enabled);
method public androidx.wear.watchface.ComplicationSlot.Builder setFixedComplicationDataSource(boolean fixedComplicationDataSource);
}
diff --git a/wear/watchface/watchface/api/public_plus_experimental_current.txt b/wear/watchface/watchface/api/public_plus_experimental_current.txt
index a8af996..1685908 100644
--- a/wear/watchface/watchface/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface/api/public_plus_experimental_current.txt
@@ -34,7 +34,7 @@
method @UiThread public androidx.wear.watchface.complications.ComplicationSlotBounds getComplicationSlotBounds();
method public android.os.Bundle getConfigExtras();
method @UiThread public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getDefaultDataSourcePolicy();
- method @UiThread public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
+ method @Deprecated @UiThread public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
method public boolean getFixedComplicationDataSource();
method public int getId();
method public boolean getInitiallyEnabled();
@@ -45,6 +45,7 @@
method @UiThread public boolean isEnabled();
method @UiThread public void render(android.graphics.Canvas canvas, java.time.ZonedDateTime zonedDateTime, androidx.wear.watchface.RenderParameters renderParameters);
method @UiThread public void renderHighlightLayer(android.graphics.Canvas canvas, java.time.ZonedDateTime zonedDateTime, androidx.wear.watchface.RenderParameters renderParameters);
+ method public void setConfigExtras(android.os.Bundle value);
property @UiThread public final int accessibilityTraversalIndex;
property public final int boundsType;
property public final androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory;
@@ -52,7 +53,7 @@
property @UiThread public final androidx.wear.watchface.complications.ComplicationSlotBounds complicationSlotBounds;
property public final android.os.Bundle configExtras;
property @UiThread public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy;
- property @UiThread public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
+ property @Deprecated @UiThread public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
property @UiThread public final boolean enabled;
property public final boolean fixedComplicationDataSource;
property public final int id;
@@ -67,7 +68,7 @@
method public androidx.wear.watchface.ComplicationSlot build();
method public androidx.wear.watchface.ComplicationSlot.Builder setAccessibilityTraversalIndex(int accessibilityTraversalIndex);
method public androidx.wear.watchface.ComplicationSlot.Builder setConfigExtras(android.os.Bundle extras);
- method public androidx.wear.watchface.ComplicationSlot.Builder setDefaultDataSourceType(androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType);
+ method @Deprecated public androidx.wear.watchface.ComplicationSlot.Builder setDefaultDataSourceType(androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType);
method public androidx.wear.watchface.ComplicationSlot.Builder setEnabled(boolean enabled);
method public androidx.wear.watchface.ComplicationSlot.Builder setFixedComplicationDataSource(boolean fixedComplicationDataSource);
}
diff --git a/wear/watchface/watchface/api/restricted_current.txt b/wear/watchface/watchface/api/restricted_current.txt
index 958905b..4b8c715 100644
--- a/wear/watchface/watchface/api/restricted_current.txt
+++ b/wear/watchface/watchface/api/restricted_current.txt
@@ -34,7 +34,7 @@
method @UiThread public androidx.wear.watchface.complications.ComplicationSlotBounds getComplicationSlotBounds();
method public android.os.Bundle getConfigExtras();
method @UiThread public androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy getDefaultDataSourcePolicy();
- method @UiThread public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
+ method @Deprecated @UiThread public androidx.wear.watchface.complications.data.ComplicationType getDefaultDataSourceType();
method public boolean getFixedComplicationDataSource();
method public int getId();
method public boolean getInitiallyEnabled();
@@ -45,6 +45,7 @@
method @UiThread public boolean isEnabled();
method @UiThread public void render(android.graphics.Canvas canvas, java.time.ZonedDateTime zonedDateTime, androidx.wear.watchface.RenderParameters renderParameters);
method @UiThread public void renderHighlightLayer(android.graphics.Canvas canvas, java.time.ZonedDateTime zonedDateTime, androidx.wear.watchface.RenderParameters renderParameters);
+ method public void setConfigExtras(android.os.Bundle value);
property @UiThread public final int accessibilityTraversalIndex;
property public final int boundsType;
property public final androidx.wear.watchface.CanvasComplicationFactory canvasComplicationFactory;
@@ -52,7 +53,7 @@
property @UiThread public final androidx.wear.watchface.complications.ComplicationSlotBounds complicationSlotBounds;
property public final android.os.Bundle configExtras;
property @UiThread public final androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy defaultDataSourcePolicy;
- property @UiThread public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
+ property @Deprecated @UiThread public final androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType;
property @UiThread public final boolean enabled;
property public final boolean fixedComplicationDataSource;
property public final int id;
@@ -67,7 +68,7 @@
method public androidx.wear.watchface.ComplicationSlot build();
method public androidx.wear.watchface.ComplicationSlot.Builder setAccessibilityTraversalIndex(int accessibilityTraversalIndex);
method public androidx.wear.watchface.ComplicationSlot.Builder setConfigExtras(android.os.Bundle extras);
- method public androidx.wear.watchface.ComplicationSlot.Builder setDefaultDataSourceType(androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType);
+ method @Deprecated public androidx.wear.watchface.ComplicationSlot.Builder setDefaultDataSourceType(androidx.wear.watchface.complications.data.ComplicationType defaultDataSourceType);
method public androidx.wear.watchface.ComplicationSlot.Builder setEnabled(boolean enabled);
method public androidx.wear.watchface.ComplicationSlot.Builder setFixedComplicationDataSource(boolean fixedComplicationDataSource);
}
diff --git a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
index 5dfd948d..e569a54 100644
--- a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
+++ b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
@@ -257,10 +257,13 @@
ComplicationType.MONOCHROMATIC_IMAGE,
ComplicationType.SMALL_IMAGE
),
- DefaultComplicationDataSourcePolicy(SystemDataSources.DATA_SOURCE_DAY_OF_WEEK),
+ DefaultComplicationDataSourcePolicy(
+ SystemDataSources.DATA_SOURCE_DAY_OF_WEEK,
+ ComplicationType.SHORT_TEXT
+ ),
ComplicationSlotBounds(RectF(0.2f, 0.4f, 0.4f, 0.6f))
- ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
- .build()
+ ).build()
+
val rightComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
EXAMPLE_CANVAS_WATCHFACE_RIGHT_COMPLICATION_ID,
canvasComplicationFactory,
@@ -271,10 +274,12 @@
ComplicationType.MONOCHROMATIC_IMAGE,
ComplicationType.SMALL_IMAGE
),
- DefaultComplicationDataSourcePolicy(SystemDataSources.DATA_SOURCE_STEP_COUNT),
+ DefaultComplicationDataSourcePolicy(
+ SystemDataSources.DATA_SOURCE_STEP_COUNT,
+ ComplicationType.SHORT_TEXT
+ ),
ComplicationSlotBounds(RectF(0.6f, 0.4f, 0.8f, 0.6f))
- ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
- .build()
+ ).build()
return ComplicationSlotsManager(
listOf(leftComplication, rightComplication),
currentUserStyleRepository
diff --git a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt
index c4218f2..74939e4e 100644
--- a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt
+++ b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasDigitalWatchFaceService.kt
@@ -95,10 +95,6 @@
internal val MARGIN_FRACTION_WITHOUT_COMPLICATION = Vec2f(0.2f, 0.2f)
internal val MARGIN_FRACTION_WITH_COMPLICATION = Vec2f(0.4f, 0.4f)
-// If the lightness in HSL color space is greater than this threshold, this color would be regarded
-// as a light color.
-internal const val BACKGROUND_COLOR_LIGHTNESS_THRESHOLD = 0.5f
-
enum class ComplicationID {
UPPER,
RIGHT,
@@ -465,10 +461,6 @@
)
}
-fun Byte.toUnsigned(): Int {
- return if (this < 0) this + 256 else this.toInt()
-}
-
/** A simple example canvas based digital watch face. */
class ExampleCanvasDigitalWatchFaceService : WatchFaceService() {
// Lazy because the context isn't initialized til later.
@@ -527,15 +519,17 @@
ComplicationType.MONOCHROMATIC_IMAGE,
ComplicationType.SMALL_IMAGE
),
- DefaultComplicationDataSourcePolicy(SystemDataSources.DATA_SOURCE_WATCH_BATTERY),
+ DefaultComplicationDataSourcePolicy(
+ SystemDataSources.DATA_SOURCE_WATCH_BATTERY,
+ ComplicationType.SHORT_TEXT
+ ),
ComplicationSlotBounds(
createBoundsRect(
LEFT_CIRCLE_COMPLICATION_CENTER_FRACTION,
CIRCLE_COMPLICATION_DIAMETER_FRACTION
)
)
- ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
- .build()
+ ).build()
private val rightComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
ComplicationID.RIGHT.ordinal,
@@ -546,15 +540,17 @@
ComplicationType.MONOCHROMATIC_IMAGE,
ComplicationType.SMALL_IMAGE
),
- DefaultComplicationDataSourcePolicy(SystemDataSources.DATA_SOURCE_DATE),
+ DefaultComplicationDataSourcePolicy(
+ SystemDataSources.DATA_SOURCE_DATE,
+ ComplicationType.SHORT_TEXT
+ ),
ComplicationSlotBounds(
createBoundsRect(
RIGHT_CIRCLE_COMPLICATION_CENTER_FRACTION,
CIRCLE_COMPLICATION_DIAMETER_FRACTION
)
)
- ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
- .build()
+ ).build()
private val upperAndLowerComplicationTypes = listOf(
ComplicationType.LONG_TEXT,
@@ -568,7 +564,10 @@
ComplicationID.UPPER.ordinal,
canvasComplicationFactory,
upperAndLowerComplicationTypes,
- DefaultComplicationDataSourcePolicy(SystemDataSources.DATA_SOURCE_WORLD_CLOCK),
+ DefaultComplicationDataSourcePolicy(
+ SystemDataSources.DATA_SOURCE_WORLD_CLOCK,
+ ComplicationType.LONG_TEXT
+ ),
ComplicationSlotBounds(
ComplicationType.values().associateWith {
if (it == ComplicationType.LONG_TEXT) {
@@ -584,14 +583,16 @@
}
}
)
- ).setDefaultDataSourceType(ComplicationType.LONG_TEXT)
- .build()
+ ).build()
private val lowerComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
ComplicationID.LOWER.ordinal,
canvasComplicationFactory,
upperAndLowerComplicationTypes,
- DefaultComplicationDataSourcePolicy(SystemDataSources.DATA_SOURCE_NEXT_EVENT),
+ DefaultComplicationDataSourcePolicy(
+ SystemDataSources.DATA_SOURCE_NEXT_EVENT,
+ ComplicationType.LONG_TEXT
+ ),
ComplicationSlotBounds(
ComplicationType.values().associateWith {
if (it == ComplicationType.LONG_TEXT) {
@@ -607,8 +608,7 @@
}
}
)
- ).setDefaultDataSourceType(ComplicationType.LONG_TEXT)
- .build()
+ ).build()
private val backgroundComplication = ComplicationSlot.createBackgroundComplicationSlotBuilder(
ComplicationID.BACKGROUND.ordinal,
diff --git a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
index 518482f..1745784 100644
--- a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
+++ b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleOpenGLWatchFaceService.kt
@@ -127,10 +127,12 @@
ComplicationType.MONOCHROMATIC_IMAGE,
ComplicationType.SMALL_IMAGE
),
- DefaultComplicationDataSourcePolicy(SystemDataSources.DATA_SOURCE_DAY_OF_WEEK),
+ DefaultComplicationDataSourcePolicy(
+ SystemDataSources.DATA_SOURCE_DAY_OF_WEEK,
+ ComplicationType.SHORT_TEXT
+ ),
ComplicationSlotBounds(RectF(0.2f, 0.7f, 0.4f, 0.9f))
- ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
- .build()
+ ).build()
public override fun createUserStyleSchema() = UserStyleSchema(listOf(colorStyleSetting))
diff --git a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
index 44805cb..dd33dc2 100644
--- a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
+++ b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/KDocExampleWatchFace.kt
@@ -127,11 +127,11 @@
ComplicationType.SMALL_IMAGE
),
DefaultComplicationDataSourcePolicy(
- SystemDataSources.DATA_SOURCE_DAY_OF_WEEK
+ SystemDataSources.DATA_SOURCE_DAY_OF_WEEK,
+ ComplicationType.SHORT_TEXT
),
ComplicationSlotBounds(RectF(0.15625f, 0.1875f, 0.84375f, 0.3125f))
- ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
- .build(),
+ ).build(),
ComplicationSlot.createRoundRectComplicationSlotBuilder(
/*id */ 1,
canvasComplicationFactory,
@@ -143,11 +143,11 @@
ComplicationType.SMALL_IMAGE
),
DefaultComplicationDataSourcePolicy(
- SystemDataSources.DATA_SOURCE_STEP_COUNT
+ SystemDataSources.DATA_SOURCE_STEP_COUNT,
+ ComplicationType.SHORT_TEXT
),
ComplicationSlotBounds(RectF(0.1f, 0.5625f, 0.35f, 0.8125f))
- ).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
- .build()
+ ).build()
),
currentUserStyleRepository
)
diff --git a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt
index c2abc4f..75fb8bb 100644
--- a/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt
+++ b/wear/watchface/watchface/src/androidTest/java/androidx/wear/watchface/test/XmlDefinedUserStyleSchemaAndComplicationSlotsTest.kt
@@ -237,12 +237,17 @@
ComplicationType.RANGED_VALUE,
ComplicationType.SMALL_IMAGE
)
- assertThat(slotA.defaultDataSourceType).isEqualTo(ComplicationType.RANGED_VALUE)
assertThat(slotA.defaultDataSourcePolicy.primaryDataSource).isNull()
+ assertThat(slotA.defaultDataSourcePolicy.primaryDataSourceDefaultType)
+ .isNull()
assertThat(slotA.defaultDataSourcePolicy.secondaryDataSource).isNull()
+ assertThat(slotA.defaultDataSourcePolicy.secondaryDataSourceDefaultType)
+ .isNull()
assertThat(slotA.defaultDataSourcePolicy.systemDataSourceFallback).isEqualTo(
SystemDataSources.DATA_SOURCE_WATCH_BATTERY
)
+ assertThat(slotA.defaultDataSourcePolicy.systemDataSourceFallbackDefaultType)
+ .isEqualTo(ComplicationType.RANGED_VALUE)
assertThat(
slotA.complicationSlotBounds.perComplicationTypeBounds[
ComplicationType.SHORT_TEXT
@@ -254,16 +259,21 @@
val slotB = watchFaceImpl.complicationSlotsManager.complicationSlots[20]!!
assertThat(slotB.boundsType).isEqualTo(ComplicationSlotBoundsType.BACKGROUND)
assertThat(slotB.supportedTypes).containsExactly(
- ComplicationType.LONG_TEXT
+ ComplicationType.SHORT_TEXT, ComplicationType.LONG_TEXT
)
- assertThat(slotB.defaultDataSourceType).isEqualTo(ComplicationType.LONG_TEXT)
assertThat(slotB.defaultDataSourcePolicy.primaryDataSource).isEqualTo(
ComponentName("com.package", "com.app")
)
+ assertThat(slotB.defaultDataSourcePolicy.primaryDataSourceDefaultType)
+ .isEqualTo(ComplicationType.SHORT_TEXT)
assertThat(slotB.defaultDataSourcePolicy.secondaryDataSource).isNull()
+ assertThat(slotB.defaultDataSourcePolicy.secondaryDataSourceDefaultType)
+ .isNull()
assertThat(slotB.defaultDataSourcePolicy.systemDataSourceFallback).isEqualTo(
SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET
)
+ assertThat(slotB.defaultDataSourcePolicy.systemDataSourceFallbackDefaultType)
+ .isEqualTo(ComplicationType.LONG_TEXT)
assertThat(
slotB.complicationSlotBounds.perComplicationTypeBounds[
ComplicationType.SHORT_TEXT
diff --git a/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface.xml b/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface.xml
index fdf7dbe..240fec6 100644
--- a/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface.xml
+++ b/wear/watchface/watchface/src/androidTest/res/xml/xml_watchface.xml
@@ -33,17 +33,18 @@
app:slotId="10"
app:boundsType="ROUND_RECT"
app:supportedTypes="SHORT_TEXT|RANGED_VALUE|SMALL_IMAGE"
- app:defaultDataSourceType="RANGED_VALUE"
- app:systemDataSourceFallback="DATA_SOURCE_WATCH_BATTERY">
+ app:systemDataSourceFallback="DATA_SOURCE_WATCH_BATTERY"
+ app:systemDataSourceFallbackDefaultType="RANGED_VALUE">
<ComplicationSlotBounds app:left="0.3" app:top="0.7" app:right="0.7" app:bottom="0.9"/>
</ComplicationSlot>
<ComplicationSlot
app:slotId="20"
app:boundsType="BACKGROUND"
- app:supportedTypes="LONG_TEXT"
- app:defaultDataSourceType="LONG_TEXT"
+ app:supportedTypes="LONG_TEXT|SHORT_TEXT"
app:primaryDataSource="com.package/com.app"
- app:systemDataSourceFallback="DATA_SOURCE_SUNRISE_SUNSET">
+ app:primaryDataSourceDefaultType="SHORT_TEXT"
+ app:systemDataSourceFallback="DATA_SOURCE_SUNRISE_SUNSET"
+ app:systemDataSourceFallbackDefaultType="LONG_TEXT">
<ComplicationSlotBounds app:left="0.1" app:top="0.2" app:right="0.3" app:bottom="0.4"/>
</ComplicationSlot>
</XmlWatchFace>
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 6411050..5e60d2d 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
@@ -184,6 +184,7 @@
context.unregisterReceiver(actionBatteryLowReceiver)
context.unregisterReceiver(actionBatteryOkayReceiver)
context.unregisterReceiver(actionPowerConnectedReceiver)
+ context.unregisterReceiver(actionPowerDisconnectedReceiver)
context.unregisterReceiver(mockTimeReceiver)
}
}
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
index 8e16e8c..0b9ca50 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlot.kt
@@ -208,7 +208,8 @@
* Editors need to know the initial state of a complication slot to predict the effects of making a
* style change.
* @param configExtras Extras to be merged into the Intent sent when invoking the complication data
- * source chooser activity.
+ * source chooser activity. This features is intended for OEM watch faces where they have elements
+ * that behave like a complication but are in fact entirely watch face specific.
* @param fixedComplicationDataSource Whether or not the complication data source is fixed (i.e.
* can't be changed by the user). This is useful for watch faces built around specific
* complications.
@@ -226,7 +227,7 @@
defaultDataSourceType: ComplicationType,
@get:JvmName("isInitiallyEnabled")
public val initiallyEnabled: Boolean,
- public val configExtras: Bundle,
+ configExtras: Bundle,
@get:JvmName("isFixedComplicationDataSource")
public val fixedComplicationDataSource: Boolean,
public val tapFilter: ComplicationTapFilter
@@ -238,6 +239,17 @@
internal lateinit var complicationSlotsManager: ComplicationSlotsManager
/**
+ * Extras to be merged into the Intent sent when invoking the complication data source chooser
+ * activity.
+ */
+ public var configExtras: Bundle = configExtras
+ set(value) {
+ field = value
+ complicationSlotsManager.configExtrasChangeCallback
+ ?.onComplicationSlotConfigExtrasChanged()
+ }
+
+ /**
* The [CanvasComplication] used to render the complication. This can't be used until after
* [WatchFaceService.createWatchFace] has completed.
*/
@@ -395,7 +407,7 @@
private val id: Int,
private val canvasComplicationFactory: CanvasComplicationFactory,
private val supportedTypes: List<ComplicationType>,
- private val defaultDataSourcePolicy: DefaultComplicationDataSourcePolicy,
+ private var defaultDataSourcePolicy: DefaultComplicationDataSourcePolicy,
@ComplicationSlotBoundsType private val boundsType: Int,
private val bounds: ComplicationSlotBounds,
private val complicationTapFilter: ComplicationTapFilter
@@ -427,9 +439,38 @@
* Note care should be taken to ensure [defaultDataSourceType] is compatible with the
* [DefaultComplicationDataSourcePolicy].
*/
+ @Deprecated("Instead set DefaultComplicationDataSourcePolicy" +
+ ".systemDataSourceFallbackDefaultType.")
public fun setDefaultDataSourceType(
defaultDataSourceType: ComplicationType
): Builder {
+ defaultDataSourcePolicy = when {
+ defaultDataSourcePolicy.secondaryDataSource != null ->
+ DefaultComplicationDataSourcePolicy(
+ defaultDataSourcePolicy.primaryDataSource!!,
+ defaultDataSourcePolicy.primaryDataSourceDefaultType
+ ?: defaultDataSourceType,
+ defaultDataSourcePolicy.secondaryDataSource!!,
+ defaultDataSourcePolicy.secondaryDataSourceDefaultType
+ ?: defaultDataSourceType,
+ defaultDataSourcePolicy.systemDataSourceFallback,
+ defaultDataSourceType
+ )
+
+ defaultDataSourcePolicy.primaryDataSource != null ->
+ DefaultComplicationDataSourcePolicy(
+ defaultDataSourcePolicy.primaryDataSource!!,
+ defaultDataSourcePolicy.primaryDataSourceDefaultType
+ ?: defaultDataSourceType,
+ defaultDataSourcePolicy.systemDataSourceFallback,
+ defaultDataSourceType
+ )
+
+ else -> DefaultComplicationDataSourcePolicy(
+ defaultDataSourcePolicy.systemDataSourceFallback,
+ defaultDataSourceType
+ )
+ }
this.defaultDataSourceType = defaultDataSourceType
return this
}
@@ -561,6 +602,8 @@
/**
* The default [ComplicationType] to use alongside [defaultDataSourcePolicy].
*/
+ @Deprecated("Use DefaultComplicationDataSourcePolicy." +
+ "systemDataSourceFallbackDefaultType instead")
public var defaultDataSourceType: ComplicationType = defaultDataSourceType
@UiThread
get
@@ -740,14 +783,20 @@
writer.println(
"defaultDataSourcePolicy.primaryDataSource=${defaultDataSourcePolicy.primaryDataSource}"
)
+ writer.println("defaultDataSourcePolicy.primaryDataSourceDefaultDataSourceType=" +
+ defaultDataSourcePolicy.primaryDataSourceDefaultType)
writer.println(
"defaultDataSourcePolicy.secondaryDataSource=" +
defaultDataSourcePolicy.secondaryDataSource
)
+ writer.println("defaultDataSourcePolicy.secondaryDataSourceDefaultDataSourceType=" +
+ defaultDataSourcePolicy.secondaryDataSourceDefaultType)
writer.println(
"defaultDataSourcePolicy.systemDataSourceFallback=" +
defaultDataSourcePolicy.systemDataSourceFallback
)
+ writer.println("defaultDataSourcePolicy.systemDataSourceFallbackDefaultType=" +
+ defaultDataSourcePolicy.systemDataSourceFallbackDefaultType)
writer.println("data=${renderer.getData()}")
val bounds = complicationSlotBounds.perComplicationTypeBounds.map {
"${it.key} -> ${it.value}"
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt
index 5def43e..a51976f 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt
@@ -118,6 +118,11 @@
private val complicationListeners = HashSet<TapCallback>()
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public var configExtrasChangeCallback: WatchFace.ComplicationSlotConfigExtrasChangeCallback? =
+ null
+
@VisibleForTesting
internal constructor(
complicationSlotCollection: Collection<ComplicationSlot>,
@@ -234,11 +239,13 @@
if (complication.defaultDataSourcePolicyDirty ||
complication.defaultDataSourceTypeDirty
) {
+ // Note this is a NOP in the androidx flow.
watchFaceHostApi.setDefaultComplicationDataSourceWithFallbacks(
complication.id,
complication.defaultDataSourcePolicy.dataSourcesAsList(),
complication.defaultDataSourcePolicy.systemDataSourceFallback,
- complication.defaultDataSourceType.toWireComplicationType()
+ complication.defaultDataSourcePolicy
+ .systemDataSourceFallbackDefaultType.toWireComplicationType()
)
}
@@ -405,7 +412,8 @@
it.key,
it.value.defaultDataSourcePolicy.dataSourcesAsList(),
it.value.defaultDataSourcePolicy.systemDataSourceFallback,
- it.value.defaultDataSourceType.toWireComplicationType()
+ it.value.defaultDataSourcePolicy
+ .systemDataSourceFallbackDefaultType.toWireComplicationType()
)
}.toTypedArray()
}
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index d8e9b4e..0154045 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -254,6 +254,21 @@
/** Signals that the activity is going away and resources should be released. */
public fun onDestroy()
+
+ /** Sets a callback to observe an y changes to [ComplicationSlot.configExtras]. */
+ public fun setComplicationSlotConfigExtrasChangeCallback(
+ callback: ComplicationSlotConfigExtrasChangeCallback?
+ )
+ }
+
+ /**
+ * Used to inform EditorSession about changes to [ComplicationSlot.configExtras].
+ *
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public interface ComplicationSlotConfigExtrasChangeCallback {
+ public fun onComplicationSlotConfigExtrasChanged()
}
/**
@@ -656,6 +671,7 @@
if (!watchState.isHeadless) {
WatchFace.registerEditorDelegate(componentName, WFEditorDelegate())
+ registerReceivers()
}
val mainScope = CoroutineScope(Dispatchers.Main.immediate)
@@ -758,6 +774,12 @@
return screenShot
}
+ override fun setComplicationSlotConfigExtrasChangeCallback(
+ callback: WatchFace.ComplicationSlotConfigExtrasChangeCallback?
+ ) {
+ complicationSlotsManager.configExtrasChangeCallback = callback
+ }
+
override fun onDestroy(): Unit = TraceEvent("WFEditorDelegate.onDestroy").use {
if (watchState.isHeadless) {
this@WatchFaceImpl.onDestroy()
@@ -785,10 +807,10 @@
@UiThread
private fun registerReceivers() {
+ // Looper can be null in some tests.
require(watchFaceHostApi.getUiThreadHandler().looper.isCurrentThread) {
"registerReceivers must be called the UiThread"
}
-
// There's no point registering BroadcastsReceiver for headless instances.
if (broadcastsReceiver == null && !watchState.isHeadless) {
broadcastsReceiver =
@@ -798,6 +820,7 @@
@UiThread
private fun unregisterReceivers() {
+ // Looper can be null in some tests.
require(watchFaceHostApi.getUiThreadHandler().looper.isCurrentThread) {
"unregisterReceivers must be called the UiThread"
}
@@ -979,6 +1002,9 @@
@UiThread
internal fun getComplicationState() = complicationSlotsManager.complicationSlots.map {
+ val systemDataSourceFallbackDefaultType =
+ it.value.defaultDataSourcePolicy.systemDataSourceFallbackDefaultType
+ .toWireComplicationType()
IdAndComplicationStateWireFormat(
it.key,
ComplicationStateWireFormat(
@@ -987,7 +1013,11 @@
ComplicationType.toWireTypes(it.value.supportedTypes),
it.value.defaultDataSourcePolicy.dataSourcesAsList(),
it.value.defaultDataSourcePolicy.systemDataSourceFallback,
- it.value.defaultDataSourceType.toWireComplicationType(),
+ systemDataSourceFallbackDefaultType,
+ it.value.defaultDataSourcePolicy.primaryDataSourceDefaultType
+ ?.toWireComplicationType() ?: systemDataSourceFallbackDefaultType,
+ it.value.defaultDataSourcePolicy.secondaryDataSourceDefaultType
+ ?.toWireComplicationType() ?: systemDataSourceFallbackDefaultType,
it.value.enabled,
it.value.initiallyEnabled,
it.value.renderer.getData().type.toWireComplicationType(),
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
index 9802a0d..098c657 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
@@ -78,6 +78,8 @@
public fun setActiveComplicationSlots(complicationSlotIds: IntArray)
/**
+ * For WSL flow, not used in androidx flow.
+ *
* Accepts a list of custom complication data sources to attempt to set as the default
* complication data source for the specified watch face [ComplicationSlot] id. The custom
* complication data sources are tried in turn, if the first doesn't exist then the next one
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index 28102a7..d94318a 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -1407,6 +1407,9 @@
createComplicationSlotsManager(
CurrentUserStyleRepository(createUserStyleSchema())
).complicationSlots.map {
+ val systemDataSourceFallbackDefaultType =
+ it.value.defaultDataSourcePolicy.systemDataSourceFallbackDefaultType
+ .toWireComplicationType()
ComplicationSlotMetadataWireFormat(
it.key,
it.value.complicationSlotBounds.perComplicationTypeBounds.keys.map {
@@ -1418,7 +1421,11 @@
it.value.supportedTypes.toWireTypes(),
it.value.defaultDataSourcePolicy.dataSourcesAsList(),
it.value.defaultDataSourcePolicy.systemDataSourceFallback,
- it.value.defaultDataSourceType.toWireComplicationType(),
+ systemDataSourceFallbackDefaultType,
+ it.value.defaultDataSourcePolicy.primaryDataSourceDefaultType
+ ?.toWireComplicationType() ?: systemDataSourceFallbackDefaultType,
+ it.value.defaultDataSourcePolicy.secondaryDataSourceDefaultType
+ ?.toWireComplicationType() ?: systemDataSourceFallbackDefaultType,
it.value.initiallyEnabled,
it.value.fixedComplicationDataSource,
it.value.configExtras
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/XmlSchemaAndComplicationSlotsDefinition.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/XmlSchemaAndComplicationSlotsDefinition.kt
index 3dc4dc6..f80df82 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/XmlSchemaAndComplicationSlotsDefinition.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/XmlSchemaAndComplicationSlotsDefinition.kt
@@ -91,7 +91,6 @@
val bounds: ComplicationSlotBounds,
val supportedTypes: List<ComplicationType>,
val defaultDataSourcePolicy: DefaultComplicationDataSourcePolicy,
- val defaultDataSourceType: ComplicationType,
val initiallyEnabled: Boolean,
val fixedComplicationDataSource: Boolean
) {
@@ -164,10 +163,40 @@
attributes.getString(R.styleable.ComplicationSlot_primaryDataSource)?.let {
ComponentName.unflattenFromString(it)
}
+ val primaryDataSourceDefaultType =
+ if (attributes.hasValue(
+ R.styleable.ComplicationSlot_primaryDataSourceDefaultType
+ )
+ ) {
+ ComplicationType.fromWireType(
+ attributes.getInt(
+ R.styleable.ComplicationSlot_primaryDataSourceDefaultType,
+ 0
+ )
+ )
+ } else {
+ null
+ }
val secondaryDataSource =
attributes.getString(R.styleable.ComplicationSlot_secondaryDataSource)?.let {
ComponentName.unflattenFromString(it)
}
+
+ val secondaryDataSourceDefaultType =
+ if (attributes.hasValue(
+ R.styleable.ComplicationSlot_secondaryDataSourceDefaultType
+ )
+ ) {
+ ComplicationType.fromWireType(
+ attributes.getInt(
+ R.styleable.ComplicationSlot_secondaryDataSourceDefaultType,
+ 0
+ )
+ )
+ } else {
+ null
+ }
+
require(
attributes.hasValue(R.styleable.ComplicationSlot_systemDataSourceFallback)
) {
@@ -177,37 +206,64 @@
R.styleable.ComplicationSlot_systemDataSourceFallback,
0
)
+ require(
+ attributes.hasValue(
+ R.styleable.ComplicationSlot_systemDataSourceFallbackDefaultType
+ )
+ ) {
+ "A ComplicationSlot must have a systemDataSourceFallbackDefaultType" +
+ " attribute"
+ }
+ val systemDataSourceFallbackDefaultType = ComplicationType.fromWireType(
+ attributes.getInt(
+ R.styleable.ComplicationSlot_systemDataSourceFallbackDefaultType,
+ 0
+ )
+ )
val defaultComplicationDataSourcePolicy = when {
secondaryDataSource != null -> {
require(primaryDataSource != null) {
"If a secondaryDataSource is specified, a primaryDataSource must be too"
}
+ require(primaryDataSourceDefaultType != null) {
+ "If a primaryDataSource is specified, a " +
+ "primaryDataSourceDefaultType must be too"
+ }
+ require(secondaryDataSourceDefaultType != null) {
+ "If a secondaryDataSource is specified, a " +
+ "secondaryDataSourceDefaultType must be too"
+ }
DefaultComplicationDataSourcePolicy(
primaryDataSource,
+ primaryDataSourceDefaultType,
secondaryDataSource,
- systemDataSourceFallback
+ secondaryDataSourceDefaultType,
+ systemDataSourceFallback,
+ systemDataSourceFallbackDefaultType
)
}
- primaryDataSource != null -> DefaultComplicationDataSourcePolicy(
- primaryDataSource,
- systemDataSourceFallback
- )
+ primaryDataSource != null -> {
+ require(primaryDataSourceDefaultType != null) {
+ "If a primaryDataSource is specified, a " +
+ "primaryDataSourceDefaultType must be too"
+ }
+ DefaultComplicationDataSourcePolicy(
+ primaryDataSource,
+ primaryDataSourceDefaultType,
+ systemDataSourceFallback,
+ systemDataSourceFallbackDefaultType
+ )
+ }
- else -> DefaultComplicationDataSourcePolicy(systemDataSourceFallback)
+ else -> {
+ DefaultComplicationDataSourcePolicy(
+ systemDataSourceFallback,
+ systemDataSourceFallbackDefaultType
+ )
+ }
}
- require(
- attributes.hasValue(R.styleable.ComplicationSlot_defaultDataSourceType)
- ) {
- "A ComplicationSlot must have a defaultDataSourceType attribute"
- }
- val defaultDataSourceType = ComplicationType.fromWireType(
- attributes.getInt(
- R.styleable.ComplicationSlot_defaultDataSourceType,
- 0
- )
- )
val initiallyEnabled = attributes.getBoolean(
R.styleable.ComplicationSlot_initiallyEnabled,
true
@@ -231,7 +287,6 @@
bounds,
supportedTypesList,
defaultComplicationDataSourcePolicy,
- defaultDataSourceType,
initiallyEnabled,
fixedComplicationDataSource
)
@@ -262,7 +317,7 @@
complicationSlotInflationFactory.getCanvasComplicationFactory(it.slotId),
it.supportedTypes,
it.defaultDataSourcePolicy,
- it.defaultDataSourceType,
+ it.defaultDataSourcePolicy.systemDataSourceFallbackDefaultType,
it.initiallyEnabled,
Bundle(),
it.fixedComplicationDataSource,
@@ -280,4 +335,4 @@
currentUserStyleRepository
)
}
-}
\ No newline at end of file
+}
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 362edee..8a207d8 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
@@ -170,6 +170,8 @@
}
fun onDestroy() {
- engine = null
+ uiThreadCoroutineScope.launch {
+ engine = null
+ }
}
}
diff --git a/wear/watchface/watchface/src/main/res/values/attrs.xml b/wear/watchface/watchface/src/main/res/values/attrs.xml
index 08ce152..9254aae 100644
--- a/wear/watchface/watchface/src/main/res/values/attrs.xml
+++ b/wear/watchface/watchface/src/main/res/values/attrs.xml
@@ -49,9 +49,33 @@
<!-- Optional. First of two non-system data source ComponentNames to be tried in turn. -->
<attr name="primaryDataSource" format="string" />
+ <!-- Optional unless primaryDataSource is specified. The default [ComplicationType] for the
+ primaryDataSource. This is a subset of
+ androidx.wear.watchface.complications.data.ComplicationType. -->
+ <attr name="primaryDataSourceDefaultType" format="enum">
+ <enum name="SHORT_TEXT" value="3" />
+ <enum name="LONG_TEXT" value="4" />
+ <enum name="RANGED_VALUE" value="5" />
+ <enum name="MONOCHROMATIC_IMAGE" value="6" />
+ <enum name="SMALL_IMAGE" value="7" />
+ <enum name="PHOTO_IMAGE" value="8" />
+ </attr>
+
<!-- Optional. Second of two non-system data source ComponentNames to be tried in turn. -->
<attr name="secondaryDataSource" format="string" />
+ <!-- Optional unless secondaryDataSource is specified. The default [ComplicationType] for
+ the secondaryDataSource. This is a subset of
+ androidx.wear.watchface.complications.data.ComplicationType. -->
+ <attr name="secondaryDataSourceDefaultType" format="enum">
+ <enum name="SHORT_TEXT" value="3" />
+ <enum name="LONG_TEXT" value="4" />
+ <enum name="RANGED_VALUE" value="5" />
+ <enum name="MONOCHROMATIC_IMAGE" value="6" />
+ <enum name="SMALL_IMAGE" value="7" />
+ <enum name="PHOTO_IMAGE" value="8" />
+ </attr>
+
<!-- Required. Fallback in case none of the non-system data sources could be used.
These definitions match androidx.wear.watchface.complications.SystemDataSources. -->
<attr name="systemDataSourceFallback" format="enum">
@@ -72,7 +96,7 @@
<!-- Required. The default [ComplicationType] for the default complication data source.
This is a subset of androidx.wear.watchface.complications.data.ComplicationType. -->
- <attr name="defaultDataSourceType" format="enum">
+ <attr name="systemDataSourceFallbackDefaultType" format="enum">
<enum name="SHORT_TEXT" value="3" />
<enum name="LONG_TEXT" value="4" />
<enum name="RANGED_VALUE" value="5" />
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 edbe885..e7fa707 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
@@ -211,6 +211,7 @@
private val badStyleOption =
ListUserStyleSetting.ListOption(Option.Id("bad_option"), "Bad", icon = null)
+ @Suppress("DEPRECATION") // setDefaultDataSourceType
private val leftComplication =
ComplicationSlot.createRoundRectComplicationSlotBuilder(
LEFT_COMPLICATION_ID,
@@ -233,6 +234,7 @@
).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
.build()
+ @Suppress("DEPRECATION") // setDefaultDataSourceType
private val rightComplication =
ComplicationSlot.createRoundRectComplicationSlotBuilder(
RIGHT_COMPLICATION_ID,
@@ -256,6 +258,7 @@
.build()
private val edgeComplicationHitTester = mock<ComplicationTapFilter>()
+ @Suppress("DEPRECATION") // setDefaultDataSourceType
private val edgeComplication =
ComplicationSlot.createEdgeComplicationSlotBuilder(
EDGE_COMPLICATION_ID,
@@ -279,6 +282,7 @@
).setDefaultDataSourceType(ComplicationType.SHORT_TEXT)
.build()
+ @Suppress("DEPRECATION") // setDefaultDataSourceType
private val backgroundComplication =
ComplicationSlot.createBackgroundComplicationSlotBuilder(
BACKGROUND_COMPLICATION_ID,
@@ -437,6 +441,7 @@
engineWrapper.onVisibilityChanged(true)
}
+ @Suppress("DEPRECATION") // defaultDataSourceType
private fun initEngineBeforeGetWatchFaceImpl(
watchFaceType: Int,
complicationSlots: List<ComplicationSlot>,
@@ -1755,6 +1760,7 @@
assertThat(argument.value.acceptsTapEvents).isEqualTo(true)
}
+ @Suppress("DEPRECATION") // setDefaultDataSourceType
@Test
public fun defaultComplicationDataSourcesWithFallbacks_newApi() {
val dataSource1 = ComponentName("com.app1", "com.app1.App1")
@@ -1785,6 +1791,7 @@
)
}
+ @Suppress("DEPRECATION") // setDefaultDataSourceType
@Test
public fun defaultComplicationDataSourcesWithFallbacks_oldApi() {
val dataSource1 = ComponentName("com.app1", "com.app1.App1")
@@ -2142,6 +2149,7 @@
assertFalse(rightComplication.enabled)
}
+ @Suppress("DEPRECATION") // setDefaultDataSourceType
public fun UserStyleManager_init_applies_ComplicationsUserStyleSetting() {
val complicationSlotId1 = 101
val complicationSlotId2 = 102
@@ -3021,6 +3029,7 @@
)
}
+ @Suppress("DEPRECATION") // DefaultComplicationDataSourcePolicyAndType
@Test
public fun canvasComplication_onRendererCreated() {
val leftCanvasComplication = mock<CanvasComplication>()
@@ -3059,6 +3068,7 @@
verify(rightCanvasComplication).onRendererCreated(renderer)
}
+ @Suppress("DEPRECATION") // DefaultComplicationDataSourcePolicyAndType
@Test
public fun complicationSlotsWithTheSameRenderer() {
val sameCanvasComplication = mock<CanvasComplication>()
diff --git a/wear/wear/build.gradle b/wear/wear/build.gradle
index 36abd85..c198025 100644
--- a/wear/wear/build.gradle
+++ b/wear/wear/build.gradle
@@ -52,6 +52,10 @@
androidTest.assets.srcDirs += project.rootDir.absolutePath + "/../../golden/wear/wear"
}
+ buildTypes.all {
+ consumerProguardFiles "proguard-rules.pro"
+ }
+
// Use Robolectric 4.+
testOptions.unitTests.includeAndroidResources = true
}
diff --git a/wear/wear/proguard-rules.pro b/wear/wear/proguard-rules.pro
new file mode 100644
index 0000000..1fb72ae
--- /dev/null
+++ b/wear/wear/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Copyright (C) 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.
+
+# Keep the classes and their public methods used to implement ambient as these
+# are called by the framework.
+-keep,allowoptimization class androidx.wear.ambient.* { public *; }
diff --git a/window/window/src/androidTest/java/androidx/window/layout/SidecarWindowBackendIntegrationTest.kt b/window/window/src/androidTest/java/androidx/window/layout/SidecarWindowBackendIntegrationTest.kt
index 760916f..2834b25 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/SidecarWindowBackendIntegrationTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/SidecarWindowBackendIntegrationTest.kt
@@ -194,9 +194,10 @@
@Test
public fun testVersionSupport() {
// Only versions 1.0 and 0.1 are supported for now
- var version = SidecarCompat.sidecarVersion
+ val version = SidecarCompat.sidecarVersion
if (version != null) {
- assertEquals(Version.VERSION_0_1, version)
+ val validVersion = Version.VERSION_0_1 == version || Version.VERSION_1_0 == version
+ assertTrue("Version must be either 1.0 or 0.1", validVersion)
}
}